oficina / home Public
Directory store implementation slice
Source design: approved Home fa64e286990762aecc6913bb9076a3cb75b088c3, discussion24. This branch implements only the directory-owned additive schema, sealed-name helper and membership-derived lookup. Discussion25 owns code review. No runtime schema merge, route, UI, owner/provisioning write entrypoint, roster HTTP or deployment is included.
internal/home/directorydata.Schema is a separate migration namespace. Future integration merges it into runtime.go directorySchema, never credential BootSchema. It creates teams, team_instances and memberships only. Teams have AUTOINCREMENT IDs, unique sqid/label, sealed name, creation time and per-team epoch. No people/address/credential schema is applied. ID allocation and name sealing belong in the future audited caller transaction; the helper never inserts a team or owner. The test fixture reserves a row with a non-plaintext placeholder, seals with the allocated ID and updates it before commit.
New(db, sealer, Config{Apps, ValidateRoute}) copies the compiled app slug list. A required caller route-validation policy enforces provisioning origin/consumer rules; production may derive the canonical hostname while a reviewed isolated pilot may use explicit origins. The store enforces canonical HTTPS origins and bounded identifiers first. This avoids duplicating every authoritative directory row in immutable process configuration, which the approved design does not require. The caller owns the policy closure and must keep it safe for concurrent reads.
TeamForMember(ctx, personRef, sqid) returns only an existing membership, with the same fixed error for foreign and unknown teams. Choices(ctx, personRef, app) returns current membership-derived live instances ordered by immutable team ID; pending/disabled instances are omitted. Invalid stored configuration refuses the entire list. The read cap is 10,000 matching rows, with overflow refused rather than a partial list. These are navigation facts, not item grants or a roster cache. Every call queries current records; membership removal is reflected without a process restart.
SealTeamName(teamID, name) authenticates up to 256 UTF-8 bytes under purpose directory.team-name/<immutable integer ID>. No plaintext name is bound to SQL by store code. Caller writes only the returned bytes. Errors are fixed and do not wrap lower-level messages or user values. DNS label reservation and sqid allocation remain future provisioning responsibilities; this read store checks their structural form.
Validation: full make test passed, plus go test -race ./internal/home/directorydata. Tests cover zero/one/two-team isolation, owner/member vocabulary, removal, disabled/pending instances, no automatic membership, raw DB/WAL/SHM plaintext scan, swapped/corrupt name ciphertext refusal, ID non-reuse, migration idempotency and schema constraints, route-policy rejection, unknown lifecycle, whole-list refusal and error privacy. Two Go overlay mutations failed the intended tests: removing the subject predicate exposes the other team's choice, and removing ID-specific sealing accepts a swapped name. Artifacts /tmp/oficina-directory-store-test.log and /tmp/oficina-directory-store-mutations. Independent code review and actual branch CI remain required; no canary is claimed for this unmounted package.