fix(identity): retain group provenance and prepare sealed recovery custody
Some checks failed
Some checks failed
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
This commit is contained in:
parent
878abc9358
commit
4f65e41ac5
22 changed files with 1685 additions and 89 deletions
114
docs/service-group-provenance.md
Normal file
114
docs/service-group-provenance.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# Durable service group creation provenance
|
||||
|
||||
This source change narrows the existing authenticated identity-provisioner
|
||||
`/v1/groups/ensure` and `/v1/groups/revoke` endpoints. It introduces no new
|
||||
principal, credential, policy decision, or rollback authorization. An endpoint's
|
||||
service token and a journal receipt do not establish an accepted case, caller
|
||||
session, owner assent, or permission to use a protected execution lane.
|
||||
|
||||
## Request and receipt migration
|
||||
|
||||
Ensure keeps `groups`, `members`, `idempotency_key` and `correlation_id`.
|
||||
The original correlation is now part of its immutable binding: the original
|
||||
idempotency key with a different correlation or group/member request is refused.
|
||||
The response adds `ensure_idempotency_key`, `ensure_correlation_id`,
|
||||
`created_groups` (name and exact directory id) and `created_memberships`
|
||||
(name, directory id and username). The owner journal independently retains the
|
||||
complete prestate and expected postimage. Response inventories are advisory;
|
||||
caller-supplied inventories are rejected and never authorize undo.
|
||||
|
||||
Revoke retains the original group/member request, its own `idempotency_key` and
|
||||
`correlation_id`, and optional boolean `delete_if_empty`. It additionally requires
|
||||
`ensure_idempotency_key` and `ensure_correlation_id` from the original ensure.
|
||||
Legacy revoke requests fail before directory access. No old deployment inventory
|
||||
is imported as proof of creation: pre-upgrade groups and memberships cannot be
|
||||
rolled back automatically without independent owner reconciliation.
|
||||
|
||||
An ensure replay returns its original receipt and never recreates subsequently
|
||||
removed state. Reusing its key after rollback is refused. Revoke replays return
|
||||
its original outcome; changing deletion options after completion is refused.
|
||||
There are at most 16 groups, 256 requested membership entries, 4096 retained
|
||||
preexisting members per operation, and 256 journal records by default. Records
|
||||
have no timeout or automatic eviction. Capacity exhaustion stops new operations.
|
||||
|
||||
## Storage policy and local fixture configuration
|
||||
|
||||
The implemented SQLite backend is for local development and disposable tests.
|
||||
It is not an admitted production storage design. Accepted
|
||||
[ADR-0015 section3](adr/ADR-0015-netkingdom-railiance-workload-packaging-and-relational-platform.md)
|
||||
requires production relational storage through CloudNative PG and `rapp-postgres`
|
||||
using `database-per-consumer`; it explicitly rejects per-workload SQLite-on-a-PVC
|
||||
as a production pattern. The rail-kubernetes wave-1 contract also does not supply
|
||||
a generic persistent-storage contract. This source does not authorize adding a
|
||||
SQLite PVC to identity-provisioner.
|
||||
|
||||
Production group operations remain unadmitted and must stay disabled until the
|
||||
existing owner paths admit a CNPG-backed implementation of the same journal
|
||||
semantics, credentials, storage/backup/restore and writer-isolation contracts,
|
||||
or an explicit governed exception to ADR-0015 identifies the exact scope,
|
||||
limits, duration, recovery and risk owner. No such backend, exception or native
|
||||
assent is supplied here. `railiance-platform` owns shared database/custody
|
||||
governance; `rapp-postgres` owns the package consumer provisioning surface.
|
||||
The identity-provisioner runtime package/operating assignment must also be
|
||||
explicitly settled rather than inferred from User Engine's dependency on it.
|
||||
|
||||
For local/disposable tests only, `GROUP_PROVENANCE_DB` selects an absolute,
|
||||
non-symlink SQLite path in a private test directory owned by the process UID,
|
||||
with mode0700 for the directory and mode0600 for the database. The local fixture
|
||||
explicitly initializes a genuinely new empty store with
|
||||
`GroupProvenance(absolute_path, initialize=True)`; initialization refuses an
|
||||
existing file. Ordinary opening never recreates a missing store. The completed
|
||||
disposable fixture uses private container tmpfs and preserves the file across
|
||||
separate Python processes; that proves process-restart semantics, not production
|
||||
durability. `/tmp`, `emptyDir`, tmpfs and an image layer do not satisfy native
|
||||
retention or recovery duties.
|
||||
|
||||
With no configuration, ordinary directory readiness reports
|
||||
`service_groups: disabled` and group endpoints refuse work. Missing/untrusted or
|
||||
unavailable configured local storage prevents startup or returns a fixed
|
||||
`group_provenance` dependency failure without path/database diagnostics.
|
||||
`service_groups: available` checks capability only: the implementation does not
|
||||
infer or enforce production admission, policy exceptions, accepted cases or
|
||||
writer isolation from a filesystem path.
|
||||
|
||||
The existing reference Deployment remains one `Recreate` replica at UID/GID10001
|
||||
with `readOnlyRootFilesystem: true`, no provenance volume and an older immutable
|
||||
image. Image rebuild, native owner review and credential delivery remain separate
|
||||
acts. Preserve its nonroot/read-only boundaries. Native retention and recovery
|
||||
must preserve original idempotency bindings, prestate, confirmed creations and
|
||||
unresolved intents; losing a journal never authorizes replacement initialization
|
||||
and replay. No arbitrary JSON import, receipt expiry or automatic uncertainty
|
||||
clearing is introduced. Owner reconciliation needs independent evidence.
|
||||
|
||||
## Safety and remaining native acceptance
|
||||
|
||||
Before each directory mutation, durable intent is committed. A successful
|
||||
response and exact readback are required before recording a confirmed creation.
|
||||
A definite failure before any mutation attempt retains a `refused` record and
|
||||
releases the active fence; its original key still cannot retry. Transport loss,
|
||||
readback failure after an attempted mutation or a process exit leaves pending/uncertain state;
|
||||
it survives reopening and blocks new mutations instead of becoming retry success.
|
||||
Every database connection rechecks the private path and original device/inode;
|
||||
missing/replaced storage is refused. Every intent update must affect exactly one
|
||||
matching journal row before execution can continue.
|
||||
The journal records mutation intent and confirmed outcomes, not an atomic LDAP
|
||||
transaction or a complete audit emission guarantee.
|
||||
|
||||
Rollback validates every targeted group id and complete member postimage before
|
||||
its first destructive operation, and verifies each subsequent change. It removes
|
||||
only memberships confirmed newly added by that ensure. It deletes only groups
|
||||
confirmed newly created by that ensure, and only while empty. Preexisting groups,
|
||||
including empty groups, are never deleted. Existing members are never included
|
||||
in the removal inventory. Detected shared-member drift or group replacement
|
||||
refuses the operation before any destructive call and retains uncertainty.
|
||||
|
||||
LLDAP exposes no membership-generation token or conditional mutation/CAS here.
|
||||
Readback cannot detect a remove/re-add ABA sequence or close a read/write race
|
||||
with an external writer. Native rollback therefore still requires an owner-proven
|
||||
writer isolation/fencing contract or a provider-side versioned/CAS operation;
|
||||
these synthetic fixtures do not prove that boundary. A durable SQLite file is
|
||||
trusted local owner-held state, not a production storage admission, tamper-evident
|
||||
remote evidence or an authorization broker. Existing caller authentication is unchanged; case/source/principal binding,
|
||||
per-attempt protected session revocation, independently admitted evidence callbacks,
|
||||
native storage/backup recovery and native endpoint acceptance remain outstanding
|
||||
under NK-WP-0048-T14/T16. This source change alone does not finish those tasks.
|
||||
Loading…
Add table
Add a link
Reference in a new issue