Specify a native Linux operator custody and durable floor backend
1 file changed,
+52
−29
implementation/home-provisioner.md+52 −29
CI passed — run details
diff --git a/implementation/home-provisioner.md b/implementation/home-provisioner.md| index e4c6860..2551d2a 100644 |
| --- a/implementation/home-provisioner.md |
| +++ b/implementation/home-provisioner.md |
| @@ -17,7 +17,7 @@ lookup and `requireFreshAuth`, in that order. The reviewed maintenance request |
| binds its immutable encrypted input and selected artifact/config/instance. The |
| local signer cannot bypass that gate. A bearer has no invented auth_time. Source: |
| platform dcf0cad `internal/console/stepup.go`, and platform117 revised maintenance |
| -design (currently e899a47; wire API remains under review). |
| +design (currently 0c59ea2; wire API remains under review). |
| A separately generated provisioner signing key attests the operator's selected |
| installation baseline and collected evidence. Both Home roles pin this public key |
| @@ -39,20 +39,40 @@ exception; retries and ordinary startup cannot repeat it. |
| ## Registry custody and persistence |
| -The operator runs `oficina provision init --state <directory>` once. Require an |
| -empty regular directory on a persistent local filesystem; create with mode0700, |
| -refuse symlinks, network filesystems and a preexisting registry. Init creates a |
| -random device ID, registry ID, signer key and encryption key in an explicitly |
| -selected OS keychain, then creates the registry. No secret is printed or committed. |
| -The first human-operated build supports macOS Keychain through pinned |
| -[github.com/keybase/go-keychain v0.0.1](https://github.com/keybase/go-keychain/tree/v0.0.1), |
| -using its native Security-framework calls, with no secret-bearing security-command |
| -argv. This separate local CLI may use cgo; Home itself remains CGO_ENABLED=0. |
| -Unsupported platforms refuse rather than saving plaintext key files. A Linux |
| -Secret Service adapter is separate work and must pass the same custody tests. Test |
| -backends exist only in disposable tests. Keys are separate entries bound to the |
| -registry/device ID. A DB copied without its corresponding keychain identity is |
| -not an initialized registry. |
| +The first supported workflow runs natively on this Linux operator workspace. |
| +`oficina provision init --state <directory> --floor-dir <independent-directory>` |
| +requires empty regular directories on persistent local ext4/XFS filesystems, |
| +mode0700, no symlinks or shared/network filesystem. The floor directory must be |
| +outside the state directory and its backup scope. Init creates random device and |
| +registry IDs plus signing/encryption keys, and stores the keys only in an age |
| +scrypt-encrypted credential file. Unlock through a private TTY prompt or explicit |
| +private password FD, never an argv/env value. Keys remain in process memory only |
| +while the command runs. No secret is printed or committed. Initialization writes a signed initializing |
| +genesis and encrypted key file before creating the registry; an interrupted init |
| +can resume only that exact genesis after unlocking its keys. It never silently |
| +replaces an existing key, registry ID or partially created floor. |
| + |
| +The independent floor backend writes signed immutable sequence/head files with |
| +exclusive creation, file fsync and parent-directory fsync; a durable head marker |
| +is updated by rename under the process lock only after the immutable file is |
| +complete. It stores no secret and never appears inside a registry backup. Bind |
| +registry/device IDs and signer public-key fingerprint in the credential file, |
| +registry and floor genesis. Missing or mismatched identity refuses. On open, validate all bounded immutable |
| +floor records and their contiguous signed chain, not only the replaceable head |
| +marker; an old marker cannot hide a newer completed floor file. Linux filesystem |
| +semantics are tested directly with process/crash injection on a supported local |
| +filesystem; an in-memory backend is insufficient. Unsupported filesystem types |
| +refuse. Copying only state or rolling back its encrypted keys cannot roll back the |
| +independent floor. A disk failure losing both is an unrecoverable condition in this |
| +slice, not permission to initialize again. |
| + |
| +An optional macOS backend can use pinned |
| +[github.com/keybase/go-keychain v0.0.1](https://github.com/keybase/go-keychain/tree/v0.0.1) |
| +through native Security-framework calls. It must satisfy the identical floor |
| +contract before being supported; this is not a prerequisite for the Linux pilot. |
| +Home itself remains CGO_ENABLED=0. No secret-bearing security-command argv or |
| +plaintext-key fallback exists. Keys and floor identity are bound to one operator |
| +device. Copying them to a second device is not a supported concurrent writer. |
| Use a dedicated local SQLite registry outside Home's database, deployment directory |
| and backup schedule. Acquire an exclusive process lock for the entire operation. |
| @@ -76,16 +96,16 @@ then append outcome evidence after receiving the exact private result. An ambigu |
| remote result leaves an in-flight journal record; it never causes an automatic |
| second Home operation. |
| -The keychain also holds the latest registry sequence and signed head digest as a |
| -rollback floor. A new head follows a two-phase local protocol: commit a signed |
| -prepared record with old/new floor to SQLite and fsync; CAS the keychain floor from |
| +The independent floor backend holds the latest registry sequence and signed head |
| +digest outside the registry backup scope. A new head follows a two-phase local protocol: commit a signed |
| +prepared record with old/new floor to SQLite and fsync; CAS the independent floor from |
| old to new; commit finalized state and checkpoint/fsync the parent directory. Before |
| any remote dispatch, require finalized matching floor. Crash between steps resumes |
| -only the single matching prepared record. Keychain ahead without the exact prepared |
| +only the single matching prepared record. Floor ahead without the exact prepared |
| record, DB ahead without a valid adjacent preparation, competing device or any |
| -older/mismatched head refuses. No sequence reuse follows a failed write. The keychain |
| +older/mismatched head refuses. No sequence reuse follows a failed write. The floor |
| CAS primitive may be serialized by the exclusive device lock; re-read before write |
| -and refuse unexpected values. A keychain backend without durable update semantics |
| +and refuse unexpected values. A floor backend without durable update semantics |
| is unsupported. Tests inject crashes at every boundary. |
| `oficina provision backup --output <exclusive-file>` exports encrypted registry |
| @@ -97,16 +117,16 @@ Use mode0600 and atomic fsync/rename. Print only the archive digest and sequence |
| Require independent backup storage selected by the operator and a verified restore |
| drill before enrolling a non-synthetic installation. Never include a plaintext key |
| or decrypted owner address. A lost local registry does not make init available for |
| -the same keychain identity. A restored archive must meet the surviving keychain |
| +the same surviving floor identity. A restored archive must meet the surviving independent |
| floor and verify every signature/hash-chain entry; an older archive refuses. If both |
| registry and independent floor are lost, this slice cannot recover or re-enroll; |
| explicit recovery design and fresh owner confirmation are required. Export/import |
| must be human-usable commands and carry no secrets through argv, terminal or shell |
| history. Passphrases use a private terminal prompt or file descriptor. |
| -The keychain is an independent rollback floor, not protection against an attacker |
| -who controls the operator's device and signing keys. Backing up registry and keychain |
| -together and rolling both back is excluded from safe restore; document this and test |
| +The separate durable floor protects against registry-backup rollback, not an |
| +attacker controlling the operator device and unlocked signing keys. Copying the |
| +registry and floor together and rolling both back is excluded from safe restore; document this and test |
| the refusal achievable with a surviving floor. Production recovery readiness still |
| requires the independent backup/restore rehearsal, not merely a second database. |
| @@ -172,7 +192,7 @@ it. Tests must prove direct app requests cannot invoke it. |
| `oficina provision enroll` prepares a human-readable baseline plan and immutable |
| private request. It identifies existing credential/directory primary hosts, |
| canonical origins, account, existing data-key fingerprints, operator public pins, |
| -exact release/artifact/config revisions and registry identity. Use supported |
| +exact release/artifact and checked configuration bindings plus registry identity. Use supported |
| `carlos instances list`, route/version checks and maintenance preflight; |
| restart's asleep output is not exec eligibility proof. The operator's fresh approval |
| is required on the console's exact request. Existing Home databases and identities |
| @@ -189,7 +209,10 @@ are retained. No credential sign-in is fabricated for the owner. |
| that exact authorized generation once; it does not substitute another random value. |
| 3. Obtain the proposed team integer/sqid through inspect. Collect an approved sealed |
| queue request, decrypt locally only in bounded private memory, and bind its approval |
| - to the operation. For synthetic previews require the already-authenticated synthetic |
| + to the operation. The synthetic pilot instead uses an explicit operator-authored |
| + synthetic plan containing already-authenticated synthetic addresses and labels, |
| + signed in this same journal and approved through the same fresh console gate; |
| + this creates no fake queue row or alternate Home identity authority. For synthetic previews require the already-authenticated synthetic |
| identity and existing_only credential resolution. Fresh console approval runs |
| credential.owner-seed; its private signed receipt is encrypted into the journal. |
| 4. Submit directory.seed-team with that receipt, exact proposed ID, reserved label, |
| @@ -300,8 +323,8 @@ a later failure. The journal identifies it, and only the exact reviewed retry ca |
| continue. Automatic compensating deletion is prohibited. |
| Tests cover fresh-owner refusal/removal; wrong signer/role/origin/digest; cross-role |
| -stream confusion; replay after expiry versus new action after expiry; keychain/DB |
| -floor crashes; stale backups; lost keychain/registry; concurrent processes/devices; |
| +stream confusion; replay after expiry versus new action after expiry; floor/DB |
| +crashes; stale backups; lost floor/registry; concurrent processes/devices; |
| create-only baseline and initial generation; exact protected config/alias/version |
| mismatch; pending verification expiry; wrong team/token; activation replay and |
| membership/owner changes between observation and commit. Run an actual human-usable |