Local-First Identity & Policy Core - Zero-cloud authorization engine
Project description
LPIC - Local-First Identity & Policy Core
A zero-cloud, SQLite-backed identity + policy + audit engine that runs entirely locally.
What is LPIC?
LPIC is a dependency-grade authorization primitive designed for offline, air-gapped, and edge environments. It provides cryptographically sound identity management, policy-based authorization, and tamper-evident audit logging without any cloud dependencies.
Core Questions LPIC Answers
- Who is making a request?
- What resource is being accessed?
- What action is requested?
- Under what conditions should this be allowed?
- Should it be ALLOW / DENY / REQUIRE_REVIEW?
- Log every decision immutably.
What LPIC Is NOT
- ❌ OAuth / SSO / Web auth
- ❌ Token server
- ❌ Cloud IAM
- ❌ Network service
LPIC is a local authorization library that embeds into your application.
Features
1️⃣ Cryptographic Identity
- Ed25519 keypairs: Industry-standard elliptic curve cryptography
- Identity derivation: Identity ID = SHA-256(public_key)
- Signature verification: All requests must be cryptographically signed
- Local key storage: Private keys never leave the device
2️⃣ Policy-Based Authorization
Policies support:
- Subject: Identity ID or role (supports wildcards)
- Resource: Resource identifier (supports wildcards like
files/*) - Action: read, write, execute, delete, admin
- Decision: ALLOW, DENY, REQUIRE_REVIEW
- Conditions:
- Time windows
- Device restrictions
- Metadata requirements
3️⃣ Deterministic Evaluation
- Pure functions: No side effects in policy evaluation
- Canonical JSON: Deterministic serialization
- Stable ordering: Same request → same decision
- Precedence rules: DENY > REQUIRE_REVIEW > ALLOW
- Default DENY: No implicit allows
4️⃣ Tamper-Evident Audit Log
- Hash chaining: Each entry links to previous entry
- Append-only: Cannot modify past entries without detection
- Integrity verification: Cryptographic proof of tampering
- Complete history: Every decision is logged
Installation
pip install -e . --break-system-packages
Quick Start
from lpic import LPICEngine, Keypair, sign_request
# Initialize engine
engine = LPICEngine("authorization.db")
# Generate and register identity
keypair = Keypair.generate()
identity_id = engine.register_identity(keypair)
# Add authorization policy
engine.add_policy(
subject=identity_id,
resource="file://data.txt",
action="read",
decision="ALLOW",
)
# Create and authorize request
request = sign_request(
keypair=keypair,
resource="file://data.txt",
action="read",
context={},
)
decision = engine.authorize(request)
print(f"Decision: {decision}") # ALLOW
# Verify audit integrity
engine.verify_audit_integrity()
print("Audit log is intact")
engine.close()
Architecture
lpic/
├── engine.py # Public API
├── invariants.py # Runtime validation
├── errors.py # Domain exceptions
├── config.py # Constants
│
├── identity/
│ ├── keypair.py # Ed25519 key management
│ ├── identity_store.py # Identity storage
│ └── signature.py # Request signing
│
├── policy/
│ ├── model.py # Policy schema
│ ├── evaluator.py # Deterministic evaluation
│ └── conditions.py # Context constraints
│
├── audit/
│ ├── log_schema.py # Audit entry structure
│ ├── recorder.py # Decision recording
│ └── integrity.py # Hash-chain verification
│
├── db/
│ ├── connection.py # SQLite management
│ ├── schema.py # Database schema
│ └── migrations.py # Schema versioning
│
└── utils/
├── hashing.py # SHA-256 operations
├── canonical_json.py # Deterministic JSON
└── time.py # Monotonic timestamps
Usage Examples
Identity Management
from lpic import LPICEngine, Keypair
engine = LPICEngine("auth.db")
# Generate new identity
keypair = Keypair.generate()
identity_id = engine.register_identity(
keypair,
metadata={"name": "Alice", "role": "admin"}
)
# Save private key (secure storage)
keypair.save_to_file("alice.pem")
# Load existing keypair
keypair = Keypair.load_from_file("alice.pem")
Policy Management
# Allow specific identity to read specific file
engine.add_policy(
subject="abc123...",
resource="file://secret.txt",
action="read",
decision="ALLOW",
)
# Allow all identities to read public files
engine.add_policy(
subject="*",
resource="public/*",
action="read",
decision="ALLOW",
)
# Require review for admin actions
engine.add_policy(
subject="*",
resource="*",
action="admin",
decision="REQUIRE_REVIEW",
)
# Time-based access
engine.add_policy(
subject="abc123...",
resource="file://temp.txt",
action="write",
decision="ALLOW",
conditions={
"time_window": {
"start": "2025-01-01T00:00:00.000000Z",
"end": "2025-12-31T23:59:59.999999Z",
}
}
)
# Device-restricted access
engine.add_policy(
subject="abc123...",
resource="file://secure.txt",
action="read",
decision="ALLOW",
conditions={
"device_id": ["device-001", "device-002"]
}
)
Authorization
from lpic import sign_request
# Create signed request
request = sign_request(
keypair=keypair,
resource="file://data.txt",
action="read",
context={
"device_id": "device-001",
"metadata": {"reason": "data analysis"}
}
)
# Authorize
decision = engine.authorize(request)
if decision == "ALLOW":
print("Access granted")
elif decision == "DENY":
print("Access denied")
elif decision == "REQUIRE_REVIEW":
print("Manual review required")
Audit Log
# Get all audit entries
audit_log = engine.get_audit_log()
# Filter by identity
user_log = engine.get_audit_log(identity_id="abc123...")
# Filter by resource
resource_log = engine.get_audit_log(resource="file://secret.txt")
# Verify integrity
try:
engine.verify_audit_integrity()
print("Audit log is intact")
except AuditChainBrokenError:
print("WARNING: Audit log has been tampered with!")
# Get summary
summary = engine.get_audit_summary()
print(f"Total entries: {summary['total_entries']}")
print(f"Valid: {summary['is_valid']}")
Security Model
Core Security Invariants
-
Identity is cryptographically bound to keypair
- Identity ID = SHA-256(public_key)
- Cannot forge identity without private key
-
Private keys never leave local device
- Keys stored in local files or secure containers
- No network transmission
-
Policies are deterministic and pure
- Same input → same output
- No side effects
- Reproducible evaluation
-
Every authorization decision is auditable
- Complete audit trail
- Tamper detection via hash chains
- Append-only log
-
No implicit allow
- Default decision is DENY
- Explicit policies required
-
Signatures cannot be forged
- Ed25519 cryptographic signatures
- Request integrity verified
Testing
Run the comprehensive test suite:
cd lpic
pytest tests/ -v
Tests verify:
- ✅ Signature validation correctness
- ✅ Policy denies by default
- ✅ Deterministic decisions
- ✅ Audit chain integrity
- ✅ Tamper detection
- ✅ Replay determinism
- ✅ Edge case rejection
- ✅ Invariant violation detection
API Reference
LPICEngine
Main engine class for authorization.
engine = LPICEngine(db_path: str)
Identity Management:
register_identity(keypair, metadata=None) -> strget_identity(identity_id) -> dictlist_identities() -> list[dict]
Policy Management:
add_policy(subject, resource, action, decision, conditions=None) -> strget_policy(policy_id) -> dictlist_policies() -> list[dict]delete_policy(policy_id)
Authorization:
authorize(request: SignedRequest) -> strauthorize_dict(request_dict) -> str
Audit Log:
get_audit_entry(entry_id) -> dictget_audit_log(identity_id=None, resource=None) -> list[dict]verify_audit_integrity() -> boolget_audit_summary() -> dict
Utilities:
health_check() -> dictclose()
Keypair
Ed25519 keypair management.
keypair = Keypair.generate()
keypair = Keypair.load_from_file(path)
Methods:
get_identity_id() -> strget_public_bytes() -> bytesget_private_bytes() -> bytessave_to_file(path)
SignedRequest
Cryptographically signed authorization request.
request = sign_request(keypair, resource, action, context)
Attributes:
identity_id: strresource: straction: strcontext: dictsignature: bytes
Configuration
Constants are defined in lpic/config.py:
- Hash Algorithm: SHA-256
- Signature Algorithm: Ed25519
- Valid Decisions: ALLOW, DENY, REQUIRE_REVIEW
- Valid Actions: read, write, execute, delete, admin
Performance Considerations
LPIC prioritizes correctness over speed:
- Policy evaluation is O(n) where n = number of policies
- Audit log writes are sequential for integrity
- Hash chain verification is O(n) where n = number of entries
- SQLite provides excellent performance for local operations
For high-performance scenarios, cache policies and batch operations.
Limitations
- No distributed consensus: Single-node only
- No automatic sync: Must implement sync layer if needed
- SQLite limitations: ~1M writes/sec, 140 TB max database
- No automatic key rotation: Must implement key management
Why LPIC is Secure by Construction
LPIC's security is achieved through multiple layers of defense:
Immutability
- Audit log is append-only: Past entries cannot be modified without breaking the hash chain
- Hash chaining: Each entry cryptographically links to all previous entries
- Canonical serialization: Ensures identical data always produces identical hashes
- Monotonic timestamps: Time cannot go backwards, preventing temporal attacks
Deterministic Evaluation
- Pure functions: Policy evaluation has zero side effects
- Stable ordering: Policy precedence rules are explicit and consistent
- Canonical JSON: Eliminates serialization non-determinism
- No hidden state: All evaluation inputs are explicit
Cryptographic Guarantees
- Ed25519 signatures: Industry-standard elliptic curve cryptography
- Identity binding: Identity ID derived directly from public key (cannot be forged)
- SHA-256 hashing: Collision-resistant hashing for integrity
- Request signing: Every request must be cryptographically signed
Explicit Denial Model
- Default DENY: No policies = access denied
- No implicit allows: Every ALLOW must be explicitly granted
- Precedence rules: DENY always wins in conflicts
- Signature required: Unsigned requests are rejected
Runtime Invariant Validation
- Identity binding verification: Identity must match public key
- Signature validation: Every request signature is verified
- Decision validation: Only valid decisions accepted
- Format validation: Hashes, IDs, and timestamps are validated
- Chain integrity: Audit log integrity checked on demand
Design Principles
- Fail securely: Errors default to DENY
- Explicit over implicit: All security decisions are explicit
- Verifiable: All security properties can be verified
- Auditable: Complete decision history
- Offline-first: No network dependencies
These properties make LPIC suitable for:
- Air-gapped environments
- Safety-critical systems
- Compliance-sensitive applications
- Edge computing
- Embedded systems
- Security research
License
This is a reference implementation for educational and research purposes.
Contributing
This is a standalone reference implementation. For production use, conduct thorough security review and testing for your specific use case.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lpic-0.1.1.tar.gz.
File metadata
- Download URL: lpic-0.1.1.tar.gz
- Upload date:
- Size: 37.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fcc5c0e752d1555d593d1575e57b537bcd1888fe480fc1ac8b674a6e6dc9134
|
|
| MD5 |
f69882dd747dca727e818aaba734101e
|
|
| BLAKE2b-256 |
41824e59b349123ae8e4391c3350af2ecaf1afab85ece43d7b7640f2b62a0a66
|
File details
Details for the file lpic-0.1.1-py3-none-any.whl.
File metadata
- Download URL: lpic-0.1.1-py3-none-any.whl
- Upload date:
- Size: 38.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db71287fbeb89a69713f5f5483adba3d2bda0aa3b9014088daca403754329531
|
|
| MD5 |
00c4d26f35b8a57b623fc871a4e8a2aa
|
|
| BLAKE2b-256 |
cfd6768ae791cdb9ea694ac1dc4d3f61913887954e17ffd1159ba28aca0f79fe
|