Permission enforcement on startup: enforce_permissions() checks store dir
(700), user files (600), signing key, TLS key, audit.log, revoked.json.
CLI and run_server() call it before any sensitive operation.
New modules:
security.py check_store(), enforce_permissions(), print_security_check()
audit.py log_event() — append-only TSV audit log (mode 600)
revoke.py revoke(jti), is_revoked(jti) — revocation list (mode 600)
New CLI commands:
security-check Print per-check pass/warn/fail report; exit 1 on failure
revoke-token <jti|jwt> Add JTI to revocation list; accepts raw JTI or full JWT
Serve integration:
Audit log written for auth request, token issuance, and userinfo calls
Revocation checked at /userinfo; revoked tokens return 401
Docs: security model section in LocalIdentity.md — threat model,
assumptions, non-guarantees, SELinux/AppArmor guidance, revocation usage.
138 tests passing (34 new for Stage 4).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Follows resolved decisions D4 and D5 (2026-03-01, Tegwick):
D4 — ESO chosen as secret injection strategy. NK-WP-0001 T01 Phase 0b
updated to specify ESO; T01 done-criteria updated to require a working ESO
test injection.
D5 — Local Identity implemented in-repo (not a separate repo). Four
deliverables:
- docs/LocalIdentity.md: capability overview, design principles, user
schema, OIDC provider description, risk mitigations, scope boundaries
- workplans/NK-WP-0002-local-identity.md: four-stage implementation plan
(core file store, bootstrap integration, minimal OIDC, security hardening)
with State Hub task IDs
- NK-WP-0001 updated: D2/D4/D5 rows resolved, T07 bootstrap section now
references NK-WP-0002 and documents the export→Keycloak migration path,
Open Questions condensed to two remaining artefacts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>