docs: design Project A auth runtime projection
This commit is contained in:
@@ -0,0 +1,424 @@
|
|||||||
|
# Project A Server Authentication Runtime Projection Design
|
||||||
|
|
||||||
|
**Date:** 2026-08-21
|
||||||
|
**Status:** approved
|
||||||
|
**Scope:** Project A server-only authentication storage and publication
|
||||||
|
**Source baseline:** `042af932ee7b686e8435aa1c5857a12c225e03c3`
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
Project A must use local ThothII authentication without creating a host account for the image's
|
||||||
|
numeric identity `10001:10001`. The host operator runs `tht` through `sudo`, while the core runs in
|
||||||
|
the container as `10001:10001`. Current code requires the authentication directory and files to be
|
||||||
|
owned by the effective UID of the reader. A single physical directory therefore cannot be both the
|
||||||
|
root-operated host store and the container-readable runtime store.
|
||||||
|
|
||||||
|
This design separates one authoritative, root-only canonical store from a read-only runtime
|
||||||
|
projection. Every successful `tht auth` mutation publishes and verifies a complete runtime
|
||||||
|
generation automatically. The core never reads the canonical store and never observes a mixed
|
||||||
|
`auth.yaml`/`users.yaml` pair.
|
||||||
|
|
||||||
|
The change is opt-in and server-only. Existing Mac, Windows, and local Linux installations without
|
||||||
|
a runtime projection retain their current files, CLI grammar, ownership rules, and behavior.
|
||||||
|
|
||||||
|
## 2. Non-goals and authorization boundary
|
||||||
|
|
||||||
|
This source change does not:
|
||||||
|
|
||||||
|
- start Project A or create any Project A container, network, or volume;
|
||||||
|
- modify or reload Nginx;
|
||||||
|
- stop, alter, or remove the legacy ThothII stack;
|
||||||
|
- change DWH, ETL, Supabase, Authentik, Aritmolab, `dwh-auth`, or another shared service;
|
||||||
|
- create a host user or group for numeric identity `10001`;
|
||||||
|
- migrate legacy sessions, Qdrant data, Ollama data, or application settings;
|
||||||
|
- expose passwords, password hashes, complete user records, or authentication YAML in logs.
|
||||||
|
|
||||||
|
Implementation and automated verification use only the isolated source worktree and temporary
|
||||||
|
fixtures. Applying the new contract to `/srv/thothii` remains a separate, explicitly authorized
|
||||||
|
pre-start operation.
|
||||||
|
|
||||||
|
## 3. Installation contract
|
||||||
|
|
||||||
|
The installation descriptor gains an optional nested runtime projection:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
profile: server
|
||||||
|
authentication:
|
||||||
|
configDirectory: /srv/thothii/operator/auth
|
||||||
|
runtimeProjection:
|
||||||
|
directory: /srv/thothii/secrets/auth-runtime
|
||||||
|
uid: 10001
|
||||||
|
gid: 10001
|
||||||
|
```
|
||||||
|
|
||||||
|
`authentication.configDirectory` remains the canonical store and must equal
|
||||||
|
`THT_AUTH_CONFIG_ROOT`. When `runtimeProjection` is present:
|
||||||
|
|
||||||
|
- the installation profile must be `server`;
|
||||||
|
- the host platform must be Linux for mutation and publication operations;
|
||||||
|
- `directory` must be an absolute canonical path distinct from `configDirectory`;
|
||||||
|
- `uid` and `gid` must both equal decimal `10001`;
|
||||||
|
- `directory` must equal `THT_AUTH_RUNTIME_ROOT` from the installation environment;
|
||||||
|
- `sudo tht` is the only supported writer and publisher;
|
||||||
|
- failure to establish effective UID 0 before a mutation or publication fails before changing
|
||||||
|
canonical or runtime state.
|
||||||
|
|
||||||
|
Projected server `tht setup`, including `--configure-only`, must also run with effective UID 0. It
|
||||||
|
creates the canonical root and files directly as `root:root 0700/0600`; it must not first create an
|
||||||
|
operator-owned store that a later `sudo tht` invocation cannot validate.
|
||||||
|
|
||||||
|
When `runtimeProjection` is absent, `THT_AUTH_RUNTIME_ROOT` must also be absent and the existing
|
||||||
|
single-directory behavior remains unchanged.
|
||||||
|
|
||||||
|
The descriptor exposes separate accessors for the canonical authentication directory and the
|
||||||
|
optional runtime-projection specification. Generic host validation continues to validate the
|
||||||
|
canonical store against the effective host UID. Pre-start commands for a projected server
|
||||||
|
additionally require a ready runtime projection consistent with the canonical revision.
|
||||||
|
|
||||||
|
## 4. Filesystem model
|
||||||
|
|
||||||
|
### 4.1 Canonical store
|
||||||
|
|
||||||
|
The authoritative store remains compatible with the existing host auth implementation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<configDirectory>/
|
||||||
|
auth.yaml
|
||||||
|
users.yaml # local mode only
|
||||||
|
.auth.lock
|
||||||
|
.auth-transaction.lock
|
||||||
|
```
|
||||||
|
|
||||||
|
The directory is `root:root 0700`; all regular files are `root:root 0600`, have link count one,
|
||||||
|
and are reached only through canonical, non-symlinked paths. `auth.yaml` and `users.yaml` retain
|
||||||
|
their current strict schemas. The canonical store is the only data restored from an authentication
|
||||||
|
backup and the only state accepted as authority by recovery.
|
||||||
|
|
||||||
|
### 4.2 Runtime projection
|
||||||
|
|
||||||
|
The projection has this stable bind-mounted root:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<runtimeProjection.directory>/
|
||||||
|
CURRENT
|
||||||
|
generations/
|
||||||
|
<generation-id>/
|
||||||
|
manifest.json
|
||||||
|
auth.yaml
|
||||||
|
users.yaml # local mode only
|
||||||
|
```
|
||||||
|
|
||||||
|
The projection root, `generations`, and generation directories are `10001:10001 0700`. All regular
|
||||||
|
files are `10001:10001 0600` with link count one. The privileged publisher sets these numeric IDs
|
||||||
|
directly; it does not resolve or create a host account. The server Compose mount is read-only, so
|
||||||
|
the core can read but cannot alter the projection even though its effective UID is `10001`.
|
||||||
|
|
||||||
|
No symlink, hardlink, device, FIFO, socket, unexpected file, permissive mode, unexpected owner, or
|
||||||
|
non-canonical path is accepted. Publishing uses descriptor-relative operations with no-follow
|
||||||
|
checks, verifies the opened object before and after I/O, and fsyncs files and parent directories
|
||||||
|
before making a generation ready.
|
||||||
|
|
||||||
|
The runtime publisher and validator live in a projection-specific Linux package whose operations
|
||||||
|
take explicit expected UID and GID values. The existing generic canonical `safeio` ownership rule
|
||||||
|
continues to require owner equals effective UID and is not weakened to accommodate the projection.
|
||||||
|
|
||||||
|
### 4.3 Generation identity
|
||||||
|
|
||||||
|
For each canonical snapshot, the publisher computes lowercase hexadecimal SHA-256 digests of the
|
||||||
|
exact bounded bytes of `auth.yaml` and, in local mode, `users.yaml`. The generation ID is the
|
||||||
|
lowercase SHA-256 of four UTF-8 lines joined by LF and terminated by one final LF:
|
||||||
|
|
||||||
|
```text
|
||||||
|
thothii-auth-projection-v1
|
||||||
|
mode=<local-or-oidc>
|
||||||
|
auth=<64-lowercase-hex>
|
||||||
|
users=<64-lowercase-hex-or-dash>
|
||||||
|
```
|
||||||
|
|
||||||
|
The generation directory name is the 64-character generation ID. For a projected installation,
|
||||||
|
the canonical revision is exactly `sha256:<generation-id>`; no second revision algorithm exists.
|
||||||
|
Its strict `manifest.json` contains only schema version `1`, generation ID, mode, that canonical
|
||||||
|
revision, exact filenames, byte sizes, and per-file SHA-256 digests. JSON is emitted canonically
|
||||||
|
with a trailing newline and no unknown or duplicate fields. Non-projected installations retain the
|
||||||
|
existing revision calculation and output for compatibility.
|
||||||
|
|
||||||
|
Generation files are immutable after publication. An existing directory with the expected ID is
|
||||||
|
reused only after complete owner, mode, type, size, content-digest, manifest, and canonical-revision
|
||||||
|
verification. Any mismatch is an integrity failure; it is never repaired in place.
|
||||||
|
|
||||||
|
`auth publish` can recover a corrupt target generation only while `CURRENT` is blocked. It
|
||||||
|
revalidates the exact directory identity and performs a bounded descriptor-relative deletion of
|
||||||
|
the whole target without following links, then publishes a newly staged complete directory under
|
||||||
|
the expected generation ID. It never edits a published generation file in place. Deletion requires
|
||||||
|
the target directory and every traversed entry to remain confined, owned by `10001:10001`, and to
|
||||||
|
have the exact allowed directory/file type, mode, link count, and bounded name. File digests are
|
||||||
|
checked when readable but are not required to match when digest failure is the reason the whole
|
||||||
|
generation is being replaced. Any structurally unsafe or unrecognized entry is not deleted
|
||||||
|
automatically and keeps the command in a sanitized integrity-failure state.
|
||||||
|
|
||||||
|
Publisher-only temporary names are closed and bounded:
|
||||||
|
|
||||||
|
- `generations/.stage-<transaction>-<generation>` is the only staging-directory form;
|
||||||
|
- `.current-<transaction>.tmp` is the only selector temporary-file form;
|
||||||
|
- no quarantine namespace exists.
|
||||||
|
|
||||||
|
After acquiring the transaction lock and before new staging, `auth publish` scans only these two
|
||||||
|
namespaces. A stage is automatically removed only when its transaction equals the transaction in a
|
||||||
|
persisted blocked selector and the complete bounded no-follow ownership/type/mode/link validation
|
||||||
|
passes. A selector temporary file may be removed for any syntactically valid transaction ID only
|
||||||
|
after the same regular-file validation and strict bounded JSON decoding pass; this covers a crash
|
||||||
|
before the blocked-selector rename. Entries with another name, an unrelated stage transaction,
|
||||||
|
unsafe metadata, excessive contents, or an invalid document remain untouched and keep recovery
|
||||||
|
blocked for explicit operator investigation. Recovery then creates a fresh transaction ID and
|
||||||
|
rebuilds its stage from the canonical store.
|
||||||
|
|
||||||
|
## 5. Atomic selection and fail-closed state
|
||||||
|
|
||||||
|
`CURRENT` is the only mutable runtime selector. It is a bounded, strict JSON document published by
|
||||||
|
same-filesystem temporary-file creation, file fsync, atomic rename, and runtime-root fsync.
|
||||||
|
|
||||||
|
A ready selector contains exactly:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"state": "ready",
|
||||||
|
"transaction": "<32-lowercase-hex>",
|
||||||
|
"generation": "<64-lowercase-hex>",
|
||||||
|
"previousGenerations": ["<zero-to-two-generation-ids>"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A blocked selector contains exactly:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"state": "blocked",
|
||||||
|
"transaction": "<32-lowercase-hex>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Transaction IDs are 128 random bits generated by the operating system. Missing, unsafe, malformed,
|
||||||
|
unknown-version, `blocked`, or internally inconsistent selectors make runtime authentication
|
||||||
|
unavailable. Only host `tht` can determine that a structurally valid projection is stale relative
|
||||||
|
to the unmounted canonical store. The backend never falls back to the canonical directory, an
|
||||||
|
older generation, an environment password, or upstream authentication.
|
||||||
|
|
||||||
|
The backend runtime provider reads and validates `CURRENT`, resolves only a safe generation name
|
||||||
|
under `generations`, and loads a complete immutable in-memory snapshot before returning: manifest,
|
||||||
|
strict authentication configuration and, in local mode, the parsed local-user registry. All
|
||||||
|
declared file digests and the generation identity are verified as part of that one load. The
|
||||||
|
projected local-registry resolver consumes the in-memory registry associated with the exact loaded
|
||||||
|
authentication snapshot; it never reopens `users.yaml` later. A request may therefore finish
|
||||||
|
against the coherent ready generation it captured immediately before a transition, even if that
|
||||||
|
generation is later removed. Every later request sees either `blocked` or the new coherent
|
||||||
|
generation.
|
||||||
|
|
||||||
|
The provider may cache an already verified complete immutable snapshot by its storage identity. It
|
||||||
|
must re-read `CURRENT` for each authentication snapshot and must not cache a `blocked` or invalid
|
||||||
|
state as ready. The in-memory snapshot is not serialized, logged, or exposed through diagnostics.
|
||||||
|
|
||||||
|
## 6. Automatic publication transaction
|
||||||
|
|
||||||
|
The following commands are mutations:
|
||||||
|
|
||||||
|
- `auth configure --mode local|oidc`;
|
||||||
|
- `auth user add`;
|
||||||
|
- `auth user set-password`;
|
||||||
|
- `auth user enable` and `auth user disable`;
|
||||||
|
- `auth user grant` and `auth user revoke`;
|
||||||
|
- `auth user logout-all`.
|
||||||
|
|
||||||
|
With a runtime projection configured, each mutation performs one serialized transaction:
|
||||||
|
|
||||||
|
1. Validate descriptor, canonical and runtime parents, ownership policy, and root privilege. For
|
||||||
|
initial configure or first publication, create missing roots with their exact required numeric
|
||||||
|
owners and modes before any canonical credential is written.
|
||||||
|
2. Acquire `.auth-transaction.lock` in the canonical root. The lock covers the complete operation
|
||||||
|
and all publication or recovery work; existing `.auth.lock` remains the inner local-registry
|
||||||
|
mutation lock.
|
||||||
|
3. Load and verify the prior canonical revision when one exists. Except for initial configure,
|
||||||
|
verify that the prior ready projection equals that canonical revision.
|
||||||
|
4. Atomically publish a new `blocked` selector with a fresh transaction ID.
|
||||||
|
5. Execute the existing canonical mutation and validate its complete resulting state.
|
||||||
|
6. Stage the complete runtime generation in the runtime root, set exact numeric ownership and
|
||||||
|
modes, fsync it, rename it to its immutable generation ID, and verify it through the same reader
|
||||||
|
used by `auth publish`.
|
||||||
|
7. Construct history as the previous ready generation followed by its prior history, de-duplicated
|
||||||
|
and truncated to two entries. Atomically publish `CURRENT` as `ready` for the new generation.
|
||||||
|
8. Re-read `CURRENT` and the complete generation, compare the runtime canonical revision with the
|
||||||
|
current canonical revision, and return success only on exact equality.
|
||||||
|
9. Remove generations not named by current plus its two-entry history, without following links.
|
||||||
|
Cleanup occurs only after the new ready state is verified. A cleanup error is reported as an
|
||||||
|
integrity failure but never rolls the selector back to an older credential state.
|
||||||
|
|
||||||
|
If the canonical mutation fails and the publisher proves that canonical bytes are unchanged, it
|
||||||
|
restores and verifies the previously saved ready selector. If canonical state changed or cannot be
|
||||||
|
proved unchanged, `CURRENT` remains `blocked` and the command fails with a sanitized recovery
|
||||||
|
instruction. If publication or final verification fails after a successful canonical mutation,
|
||||||
|
`CURRENT` remains `blocked`.
|
||||||
|
|
||||||
|
No mutating command reports success while canonical and runtime states differ.
|
||||||
|
|
||||||
|
## 7. CLI behavior and recovery
|
||||||
|
|
||||||
|
### 7.1 `tht auth publish`
|
||||||
|
|
||||||
|
`auth publish` accepts no password and no content arguments. It acquires the transaction lock,
|
||||||
|
treats the validated canonical store as authoritative, publishes `blocked`, builds or reuses the
|
||||||
|
matching immutable generation, selects it as ready, verifies equality, applies bounded retention,
|
||||||
|
and exits successfully only when the runtime projection is coherent.
|
||||||
|
|
||||||
|
It is the supported recovery command after interruption, failed publication, runtime tampering, or
|
||||||
|
restoration of a protected canonical backup. It does not recover a password, select an arbitrary
|
||||||
|
old generation, or modify canonical users.
|
||||||
|
|
||||||
|
### 7.2 `tht auth status`
|
||||||
|
|
||||||
|
Existing output remains compatible. For projected server installations, text and JSON output add
|
||||||
|
only projection state, generation ID, canonical revision, and equality status. It never emits YAML,
|
||||||
|
password hashes, user records, transaction lock contents, or environment values.
|
||||||
|
|
||||||
|
### 7.3 `tht auth check`
|
||||||
|
|
||||||
|
Before invoking the existing container diagnostic, `auth check` verifies:
|
||||||
|
|
||||||
|
- canonical auth validity;
|
||||||
|
- runtime selector and generation integrity;
|
||||||
|
- equality of canonical and projected revisions;
|
||||||
|
- exact expected owners and modes.
|
||||||
|
|
||||||
|
Only after these checks pass does it run the existing core diagnostic against the runtime mount.
|
||||||
|
Output remains bounded and sanitized. `--json` remains pristine JSON.
|
||||||
|
|
||||||
|
### 7.4 Pre-start commands
|
||||||
|
|
||||||
|
`start`, `update --check-only`, and relevant `doctor` checks fail closed when a configured runtime
|
||||||
|
projection is absent, blocked, invalid, or stale. Initial `auth configure` and `auth publish` remain
|
||||||
|
available to create or repair it. Read-only auth status remains available to diagnose a blocked
|
||||||
|
projection without exposing secrets.
|
||||||
|
|
||||||
|
### 7.5 Backup restore
|
||||||
|
|
||||||
|
A restore archive can replace canonical `auth.yaml` or `users.yaml`, so it participates in the
|
||||||
|
same projection transaction. After archive preflight identifies any
|
||||||
|
`authentication-configuration` entry and before the first restore mutation, `tht restore` must:
|
||||||
|
|
||||||
|
1. require projected-server root privilege and acquire `.auth-transaction.lock`;
|
||||||
|
2. validate the prior canonical and ready projection;
|
||||||
|
3. atomically select `blocked`;
|
||||||
|
4. perform the existing checkpointed restore or its checkpoint recovery;
|
||||||
|
5. validate the final canonical store, publish its generation, and verify equality before selecting
|
||||||
|
`ready`.
|
||||||
|
|
||||||
|
If candidate restore fails and checkpoint recovery succeeds, the recovered canonical state is
|
||||||
|
published before authentication becomes ready. If restore, checkpoint recovery, publication, or
|
||||||
|
verification cannot complete, `CURRENT` remains blocked. A restore archive with no canonical
|
||||||
|
authentication entries retains its current lifecycle and does not block authentication. A
|
||||||
|
non-projected installation retains all existing restore behavior.
|
||||||
|
|
||||||
|
## 8. Compose and backend integration
|
||||||
|
|
||||||
|
The base/local Compose contract remains unchanged: local installations mount
|
||||||
|
`THT_AUTH_CONFIG_ROOT` at `/run/thothii-auth` and use the existing direct-file provider.
|
||||||
|
|
||||||
|
Projected server installations add a dedicated
|
||||||
|
`deploy/compose.auth-runtime-projection.yaml` override after the base profile and all declared
|
||||||
|
operator overrides, but before the generated current-image override. `Installation.ComposeFiles()`
|
||||||
|
inserts this file automatically whenever `runtimeProjection` is declared. The descriptor loader
|
||||||
|
rejects manually listing the dedicated override, listing it without a projection, or otherwise
|
||||||
|
duplicating it. This ordering and automatic selection prevent an operator override from silently
|
||||||
|
restoring the canonical mount. Non-projected server installations do not include the new override
|
||||||
|
and retain their existing direct-file behavior.
|
||||||
|
|
||||||
|
The projection override:
|
||||||
|
|
||||||
|
- requires host `THT_AUTH_RUNTIME_ROOT` as the bind source;
|
||||||
|
- mounts it read-only at `/run/thothii-auth` for `core` only;
|
||||||
|
- sets container environment `THT_AUTH_RUNTIME_PROJECTION_ROOT` to the literal
|
||||||
|
`/run/thothii-auth`;
|
||||||
|
- does not mount the canonical root anywhere;
|
||||||
|
- does not add authentication environment or mounts to `workspace-maintenance`.
|
||||||
|
|
||||||
|
`THT_AUTH_RUNTIME_ROOT` is only the host-side Compose bind source.
|
||||||
|
`THT_AUTH_RUNTIME_PROJECTION_ROOT` is only the container-side provider selector and path. When the
|
||||||
|
container variable is present, backend startup and every auth snapshot use the projected provider;
|
||||||
|
the inherited `THT_AUTH_CONFIG_FILE` direct path is ignored and must retain its reviewed default
|
||||||
|
value. A conflicting direct-file value is a configuration error. When the container variable is
|
||||||
|
absent, the existing `THT_AUTH_CONFIG_FILE` behavior is unchanged. There is no runtime fallback
|
||||||
|
between the two providers.
|
||||||
|
|
||||||
|
## 9. Security properties
|
||||||
|
|
||||||
|
The implementation must preserve these invariants:
|
||||||
|
|
||||||
|
- root is the sole canonical writer and runtime publisher on a projected server installation;
|
||||||
|
- numeric `10001:10001` is a filesystem/container identity, not a new host account;
|
||||||
|
- the core receives no write-capable auth mount;
|
||||||
|
- canonical password hashes are copied only into a protected runtime generation and never logged;
|
||||||
|
- a stale projection cannot silently authorize after a canonical security mutation fails to
|
||||||
|
publish;
|
||||||
|
- a selector cannot escape the runtime root or select a partial generation;
|
||||||
|
- concurrent CLI mutations are serialized across both canonical mutation and publication;
|
||||||
|
- interrupted work leaves either the prior verified ready selector, when canonical state is proven
|
||||||
|
unchanged, or a persistent blocked selector;
|
||||||
|
- recovery always derives runtime state from the canonical store;
|
||||||
|
- local and cross-platform non-projected authentication remains behaviorally compatible.
|
||||||
|
|
||||||
|
## 10. Testing strategy
|
||||||
|
|
||||||
|
Development follows strict test-driven development: each behavior is introduced by a focused test
|
||||||
|
that fails for the intended reason before production code changes.
|
||||||
|
|
||||||
|
Required automated coverage includes:
|
||||||
|
|
||||||
|
1. Descriptor validation for valid server projection and rejection of local profile, non-Linux
|
||||||
|
mutation, non-canonical/equal paths, missing environment match, and UID/GID other than `10001`.
|
||||||
|
2. Regression coverage proving descriptors without a projection retain current Mac, Windows, and
|
||||||
|
local Linux behavior.
|
||||||
|
3. Secure publication primitives: exact owners/modes, link count, no-follow behavior, bounded
|
||||||
|
reads, fsync/rename ordering, strict pointer/manifest schemas, and digest verification.
|
||||||
|
4. Successful initial local and OIDC publication and automatic publication after every local-user
|
||||||
|
mutation.
|
||||||
|
5. Failure injection before blocking, during canonical mutation, at each stage/fsync/rename, during
|
||||||
|
final verification, and during retention; expected ready restoration or persistent blocked state
|
||||||
|
is asserted explicitly.
|
||||||
|
6. Concurrent `tht` processes proving one transaction at a time and no mixed canonical/runtime
|
||||||
|
result; rapid successive publications prove an in-flight request finishes from its complete
|
||||||
|
in-memory snapshot after the referenced generation is pruned.
|
||||||
|
7. Backend projected-provider tests for ready, blocked, absent, stale, malformed, symlinked,
|
||||||
|
hardlinked, permissive, wrong-owner, altered-manifest, altered-file, and generation-switch cases.
|
||||||
|
8. Login and session tests covering ordinary/admin login, password changes, enable/disable,
|
||||||
|
grant/revoke, and `logout-all` through immutable generations.
|
||||||
|
9. Compose rendering tests proving server core mounts only the runtime projection read-only and
|
||||||
|
`workspace-maintenance` has no authentication mount or environment; automatic override ordering,
|
||||||
|
duplicate refusal, and both host/container environment names are exact.
|
||||||
|
10. Restore tests proving authentication-bearing candidate restore, checkpoint recovery, restore
|
||||||
|
failure, and publication failure cannot leave an old ready projection; archives without auth
|
||||||
|
retain existing behavior.
|
||||||
|
11. Full Go tests with race detection and vet, backend Vitest plus TypeScript typecheck, Compose
|
||||||
|
contract tests, documentation gates, and secret scanners using only synthetic credentials.
|
||||||
|
|
||||||
|
No automated test may inspect the real `/srv/thothii` auth material, start the Project A stack, or
|
||||||
|
mutate a live service.
|
||||||
|
|
||||||
|
## 11. Documentation and later operator gate
|
||||||
|
|
||||||
|
The implementation updates server installation examples, local-auth documentation, Project A
|
||||||
|
preflight instructions, recovery guidance, and tests that enforce those documents. The operator
|
||||||
|
guide explains:
|
||||||
|
|
||||||
|
- the canonical/projection distinction in plain language;
|
||||||
|
- why no host account `10001` is created;
|
||||||
|
- exact descriptor and environment fields;
|
||||||
|
- automatic publication semantics;
|
||||||
|
- the meaning of `blocked` and recovery with `sudo tht auth publish`;
|
||||||
|
- backup and restoration of the canonical store only;
|
||||||
|
- safe verification without printing credentials or hashes.
|
||||||
|
|
||||||
|
After source implementation, review, and verification pass, applying the contract to Project A
|
||||||
|
requires a new bounded pre-start authorization. That later gate will restore the existing generated
|
||||||
|
auth data to canonical `root:root 0700/0600`, create the runtime projection through `sudo tht auth
|
||||||
|
publish`, run read-only preflights, and stop before starting the new stack unless the owner provides
|
||||||
|
separate start authorization.
|
||||||
Reference in New Issue
Block a user