# Local Home operator seed and provisioning

Status: design approved by configured Claude Fable (`claude-fable-5`, provider
event metadata verified), 2026-09-08, exact reviewed commit
898fe219f4da5092b2cc28210b344ede6a478e50. Home discussion 37 retains the verdict
and earlier quota/permission refusals. This branch starts at directory runtime171.
It defines an application package and local command protocol; the companion
`home-provisioner.md` defines the authority adapters and staging proposal. No
implementation, platform117 or live provisioning approval is implied.

## Contracts and scope

The governing sources are Home spec §7, §10, §14 and rulings 8, 22 and 23 in
`docs/superpowers/specs/2026-08-30-home-design.md`; approved directory design
fa64e286990762aecc6913bb9076a3cb75b088c3, `implementation/team-directory.md`
(Home discussion 24); approved roster contract 4e7b3ff,
`implementation/roster-contract.md` (discussion 28); and approved producer
32fb49c1d5a4f0317b7291f441f78e21b7f0a11c,
`implementation/roster-producer-storage.md` and
`internal/home/directorydata/roster_migrations/migrations/0001_roster.sql`
(discussion 33; use the migration embedded by RosterSchema if its filename
changes). Shared roster 667dc8218dd5 supplies Provisioning, SignProvisioning and
VerifyProvisioning. HTTP runtime design 5fbfdf0 is a separate integration review,
not authority for this writer. Home discussion 35 records the source audit.

The credential role alone resolves an address with its pepper. The directory
alone creates teams and memberships. An audited operator selects an approved
sealed request and its intended owner; a browser assertion, a visitor, an
instance bearer and the platform account owner alone supply no team-owner
identity proof. The operator transport authenticates the operator's authority to
run an approved action on this exact deployment. The credential receipt below
binds the intended owner across roles. This preserves the operator seed exception
in §14; it does not assert that provisioning is a browser fresh-owner ceremony.

This slice supports generation initialization when no generation or consumer
exists, a new team with its first owner, initial consumer provisioning and
activation. It provides no roster editing, token rotation, restore, owner
confirmation, key rotation, sibling owner seeding or ownership transfer command.
Those require their own reviewed operation and fresh-owner rules. In particular,
missing generation alongside existing consumers is a refusal, never a repair.

## Transport and command boundary

Platform discussion 117 owns stopping traffic and all background work, holding
the database lease, selecting the immutable executable and exact database, and
resuming safely. Its private-input/result protocol is generic. The proposed
entry point is `home operator-v1 --db <platform-selected-existing-db>`, with
standard output captured as the private result by the transport. The fixed command and
role permissions belong to reviewed deployment configuration. No request chooses
a binary, path, SQL, environment variable, shell fragment or network destination.
The command never starts an HTTP server, sends mail or makes network calls.

Read one UTF-8 JSON object from private stdin with a 64 KiB hard limit including
whitespace, then require EOF. Write one JSON result of at most 64 KiB to the
private standard-output channel only, after transaction commit. The transport
never tees or logs stdout and discards stderr. Input and result
are encrypted at rest by the transport, available only through its authenticated
private result channel, and never included in public status, receipts or logs.
The application does not implement its own remote encryption protocol.

Within the signed private wrapper defined in home-provisioner.md, operation-input
common fields are `version:1`, `operation`, and `operation_id` (random
canonical 32-byte base64url). Operation-specific closed objects are specified
below. Reject unknown or duplicate fields, trailing JSON, alternate numeric
forms, invalid UTF-8, malformed canonical base64 and oversized decoded values
before mutation. Strings are bounded before allocating their decoded contents.
No error includes supplied values. Exit 0 means a complete committed result is
available; exit 2 is fixed refusal and exit 3 unavailable/ambiguous. Neither exit
code permits automatic mutation retry. The platform queries the original result
or explicitly reruns the exact input under a fresh fence; Home idempotency then
returns the committed result without performing the action again.

The invocation must be authorized outside the request. The local adapter accepts
an immutable operation allowlist and expected role/origin/database identity from
reviewed launch configuration. A request's `operation` only selects within that
allowlist. Package constructors are private to the command adapter or receive an
opaque validated operator context; exported structs cannot fabricate that context.
The transport's operation ID is distinct from Home operation_id. Both must be
bound by the adapter's verified invocation context. Current platform source has
no signed Home invocation receipt, and this design does not assert otherwise. No body field can claim an
operator identity or maintenance state. The platform must provide an authenticated
invocation context or equivalent private launch configuration before command
wiring is approved; an environment boolean supplied by the caller is insufficient.

## Keys and durable private results

Use the existing Rastrillo family key encoding and crypto.Sign/Verify operations,
with strict canonical key decoding and derived key IDs. Reuse shared seal for
stored private envelopes. Do not copy signing arithmetic or browser assertion
validation to create operator authority.

Credential operator signing uses a separately provisioned private key and context
`oficina/home/owner-seed/v1`; the directory pins its public key outside every
receipt. Directory provisioning signing uses its separately provisioned operator
key and shared context `oficina/roster/provisioning/v1`. Roster response keys are
also distinct. Keys arrive through the existing protected per-instance config
mechanism; runtime browser assertion keys and existing data keys remain unchanged.
Ordinary startup neither creates nor rotates any of these keys. Reject identical
public keys across the roles within the command's configured material. Public
key pin replacement is an explicit future operator migration, not an input field.

Add role-specific operator result tables through additive migrations. Credential
and directory databases never receive each other's tables. Each result row has
operation_id primary key, fixed action, opaque operator reference from verified
invocation context, creation time, a keyed request digest, and a sealed result.
The request digest is HMAC-SHA256 using an independently provisioned 32-byte
idempotency key. The actual shared sealer exposes Seal/Open, not a key derivation
API. No custom KDF is introduced. Digest canonical typed operation input, not raw JSON whitespace.
This prevents a stored bare address/name digest becoming an offline dictionary.

Seal result bytes with the existing role sealer and purpose
`operator.result/v1/<exact-origin>/<operation-id>/<action>`. A result can contain
an authority-bearing receipt or bearer and therefore never appears plaintext in
SQLite/WAL, logs or public receipts. Never persist the original address/input.
Repeated exact input under the same operation ID returns the stored result;
changed semantic input or different action refuses. A lost post-commit response
therefore does not mint a new bearer, person or receipt. Keep successful result
rows until an explicit reviewed retention policy exists; no rolling cleanup may
make an old operation reusable. This initial low-volume administrative table has
a hard 10,000-operation limit, refusing further new operations at capacity.

The existing roster_operator_receipts table retains its approved public metadata
shape and SHA256 digest of the signed provisioning record; no bearer, display
name, input digest containing address material or private result belongs there.
The new directory result row and that receipt commit in the same transaction.
Private results are bounded to 48 KiB before sealing, leaving transport space.

## Operations

All origins come from validated configuration or must equal it exactly. Production
team destinations reuse the compiled registry and canonical label policy from
`directoryFeatures` and `directorydata.Config.ValidateRoute`; for Docs the origin
is `https://<label>.docs.oficina.works`. Reject alternate ports, paths, aliases,
userinfo, fragments, queries and unapproved app slugs. This design adds no pilot
manifest or per-team config exception.

`directory.inspect` accepts no payload and returns only current generation (or
explicit absent), the next proposed team integer, and derived team sqid. It writes
no authority and is not a reservation. Derive next ID from the maximum existing
team ID and sqlite_sequence, then add one, refusing signed-64-bit overflow. No
shared Home/suite codec exists today. Pin official github.com/sqids/sqids-go
v0.4.1 (upstream commit 2ba95c7920dcf08340e099c7b22beb1178da6106), using
sqids.New() and that version's fixed default alphabet, blocklist and zero minimum
length. Encode exactly one positive integer. Require Decode to return exactly
one integer in 1..MaxInt64 and Encode(decoded) to equal the original string.
This preserves the integer-encoding sqid required by §10; Docs' random document
IDs are unrelated. Golden vectors are 1=Uk, 2=gb, 3=Ef, 1000=pnd and
MaxInt64=AqkYhRmbHpEX. Refuse zero, negative/multiple numbers, alternate encodings
and reserved DNS labels independently. Dependency/options changes require explicit
compatibility review and must preserve existing team sqids. The upstream
[canonical encoding guidance](https://github.com/sqids/sqids-go/tree/v0.4.1)
requires re-encoding; a disposable local pinned-library probe verified these vectors.
The seed checks this proposal again under the write lock. A concurrent seed makes
the old proposal fail; the operator obtains a new proposal and new owner receipt.

`credential.owner-seed` accepts `directory`, `team_id`, `team_sqid`, and a private
`address` bounded to 320 UTF-8 bytes from the operator-approved decrypted queue
request. The address is transported privately into credential Home; queue
unsealing and request approval remain the external drain's duty. The command
normalizes and blind-indexes with the same credential helper as sign-in, resolves
or creates the normal opaque person stub, and never mints a session. Synthetic
live previews require already authenticated synthetic identities and an
`existing_only:true` mode; that mode refuses a missing person and sends no mail.
The directory cannot request address resolution over HTTP. Refactor the existing
findOrMintPerson SQL into a transaction-capable internal helper: its current
a.db.Writer() call cannot participate in the required operator transaction.
Keep sign-in semantics, blindIndex and day-coarsened created_at unchanged.

The result is `{version:1, operation_id, outcome:"committed", receipt}`. receipt
uses the shared closed `{v,kid,payload,signature}` envelope pattern with canonical
base64url bytes and raw 64-byte signature, at most 8 KiB total. Its signed payload
contains version, purpose="owner-seed", operation_id, credential issuer, intended
directory, proposed integer team ID and sqid, resolved person_ref, role="owner",
issued_at and expires_at. Duration is at most 300 seconds. The credential operation
and result are atomic with person-stub creation. Retry returns identical signed
bytes, even after expiry; it never renews authority. An expired receipt requires
a newly approved operation ID. The payload contains no address or team name.

`directory.initialize` accepts `expected_generation:null`. Inside its transaction
require no generation, no roster consumers, no roster tokens and no previous
bootstrap receipt; existing teams/memberships may be retained. Accept the exact random32-byte canonical base64url generation from the verified
external baseline record, persist it once and a bootstrap receipt
plus sealed result atomically. Require explicit deployment precondition that no
consumer has ever been provisioned; a database which has lost generation/receipt
history cannot satisfy this precondition based on its own empty tables. An independently
signed external installation record and its monotonic registry floor must attest
that this exact directory database has never had a roster generation provisioned.
The record issuer, signature verification and durable external registry are not
implemented by current Home or platform. The proposed companion home-provisioner.md
defines them and the explicit trusted fresh-owner baseline attestation; that
amendment requires approval before constructing initial-install authority. Platform
maintenance provides exclusion, not this Home-specific historical evidence. If
that evidence is unavailable, initialization is unavailable. Restore cannot use
this verb. Ordinary boot only applies empty schema and never invokes it.

`directory.seed-team` accepts `expected_generation`, proposed `team_id` and
`team_sqid`, canonical `label`, `display_name` (1..255 UTF-8 bytes), and the owner
`receipt` (at most 8 KiB). Check receipt signature, configured issuer/directory,
exact team proposal, fixed owner role, canonical person ref, valid issue time and
five-minute expiry. Verify time again under the write lock. Persist the consumed
credential receipt operation ID under a unique constraint in this same transaction.
Expired or malformed input makes no change. A committed exact replay returns its
stored result despite receipt expiry; changed input under either consumed owner
receipt or directory operation refuses. Distinct directory operation IDs cannot
reuse the same owner receipt.

Require generation equality, absent team/label and unchanged next ID. Allocate
using AUTOINCREMENT, verify the returned integer and corresponding sqid, seal name
under the existing ID-bound helper, insert exactly the receipt's person as first
owner, and persist result plus consumed-receipt metadata atomically. No unsealed
placeholder is written; an encrypted placeholder of the existing test/store shape
may only exist within the transaction before the final seal. Membership triggers
advance epoch; do not add a second increment. Return opaque team/sqid, generation
and resulting epoch. No team_instances row is live at this point.

`directory.provision-consumer` accepts expected generation, team sqid, app slug,
consumer ID (1..64 ASCII letters/digits/_/-), exact expected audience and expected
sequence 0 for this initial-only operation. Require an existing team with an owner,
no existing binding for team/app, no consumer ID/origin collision and exact compiled
route. Insert pending team_instance, enabled consumer, current token hash and
successful provisioning receipt in one transaction. Generate a random 32-byte
canonical bearer; store only SHA256 of its exact 43-character string in token rows.
The signed shared Provisioning has sequence 1, all configured bindings, current
generation, actual epoch and configured roster public keys (1..8); owner_confirmation
is empty for this initial generation. Verify the produced record against the
independently pinned directory operator public key before commit. Seal the private
result containing `{record, bearer}` for exact retries. Pending bindings do not
serve roster pages or appear in the chooser. No content key is copied or generated
by Home. The external provisioner creates the isolated Docs DB and its own keys.

`directory.activate-consumer` accepts expected generation, consumer ID, expected
record sequence/digest, and an opaque deployment verification operation reference.
The verified operator context must attest successful exact-host/body/header build
verification, assertion registration, isolated instance database/config, and the
reviewed TeamMode acceptance gates. A string supplied by the request is not that
attestation. Requery pending binding, consumer enabled state, token, generation and
record metadata, then switch only that existing pending binding to live and commit
its audit/result atomically. This package verifies an opaque approved activation
context; it does not fetch a URL or trust arbitrary deployment evidence JSON.
Current platform receipts do not attest all these Home-specific facts. The
external provisioner/verification adapter and its pinned evidence verifier require
the companion home-provisioner.md authority design and approval; no constructor may accept caller-controlled
booleans as a substitute. Until that adapter exists, activation remains unwired. A compatibility account-link preview is not sufficient for activation.

## Transaction and existing-database checks

Open an existing regular database with creation disabled, through the pinned
platform path. Check application identity, expected role/origin, migration ledger
and required additive schema versions before any action. Never call the general
web startup migrator to repair an unexpected DB from this command. The deployment
applies reviewed role-specific additive migrations before operator execution.

Acquire one dedicated database/sql connection. Set foreign_keys ON before beginning
and read it back as 1 on that exact connection; pooled connections cannot substitute
for this check. Also require secure_delete ON, expected journal configuration,
no foreign_key_check violations, and the verified transport fence/lease. Take an
immediate write transaction with a bounded lock timeout; all idempotency, generation,
sequence, owner receipt and registry preconditions are rechecked inside it. Do not
perform network work while holding the lock. Refuse wrong clocks, time rollback
relative to persisted operator completion time, exhausted counters or corrupt rows.
Write all related authority and audit rows together. Close/checkpoint through the
supported transport protocol before resume; an uncertain commit remains fenced
until querying the same operation establishes its outcome.

No success path changes existing sessions, assertion keys, content encryption,
existing team/member rows or unrelated consumers. Rollback leaves no orphaned team,
owner, token, receipt or result. Read-only inspect has no minting side effect.

## Review and acceptance

Implement narrow package tests before a command adapter. Exercise actual additive
schemas and sealer, signed owner receipts, actual shared provisioning verification,
and real credential normalization/blind indexing. Require meaningful failures for
FK off on the actual connection, wrong DB/role/origin, missing fence context, forged
operator context, unknown key/purpose, browser assertion substitution, stale and
future receipts, receipt reuse, raced integer proposal, changed-payload retry,
commit/output loss, name-seal failure, generation mismatch, false initial install,
consumer collision, roster epoch overflow and transaction rollback.

Extend gate 2's expensive privacy half when this seed verb lands, as required
by the Home spec. Scan SQLite, WAL, captured stdout/stderr and public receipt serialization for known
synthetic addresses, names, bearer strings, receipt payloads and private results.
Prove current Home session acceptance/signout and all existing team ciphertexts
survive applying the additive tables. Mutation tests must fail when signature,
consumed-receipt uniqueness, FK verification or idempotency checks are removed.
Demonstrate the private-stdout command with a disposable local fence fixture only; that
fixture is not evidence of platform maintenance correctness. Full repo gates, exact
CI and Fable whole-code review precede integration. Live use additionally requires
approved/shipped platform 117, operator authority and invocation/activation context
adapters, actual two-team TeamMode acceptance and canary verification.
