## ADDED Requirements ### Requirement: Shared strict document validation Configuration and secrets preview, startup, and privileged apply SHALL use the same side-effect-free validator for bounded UTF-8 YAML, duplicate keys, mapping roots, strict scalar types and bounds, known keys, exact core profile, auth-pool schemas, unique entry names, reference integrity, package capabilities, and deployment paths. #### Scenario: Valid configuration is previewed - **WHEN** an operator submits a candidate satisfying the complete schema - **THEN** preview returns normalized validation success and a bounded diff without activating the candidate #### Scenario: Duplicate or unknown key is submitted - **WHEN** a candidate contains a duplicate mapping key or unsupported field - **THEN** validation fails before staging or restart and identifies the field without echoing secret values #### Scenario: Secret reference is invalid - **WHEN** configuration selects an auth-pool entry that does not exist in the candidate secrets - **THEN** combined validation fails and neither document becomes active ### Requirement: Separate immutable package authority Worker package manifests SHALL resolve only beneath a separate root-owned, non-writable package authority, while runtime-generated initialization state, locks, and PostgreSQL credentials SHALL remain in the private writable data volume rather than the active config/secrets bind. #### Scenario: Package manifest uses the active-document directory - **WHEN** configuration references a package manifest beneath the config/secrets authority or outside the fixed package root - **THEN** validation fails before startup or privileged apply #### Scenario: Runtime initializes with active documents mounted read-only - **WHEN** the runtime initializes or restarts with the active config/secrets directory mounted read-only - **THEN** its initialization marker, singleton lock, and generated PostgreSQL password remain writable only through fixed private data paths ### Requirement: Candidate revisions and compare-and-swap The system SHALL stage validated candidate revisions separately from active files and SHALL require expected active and candidate SHA-256 identities when saving or applying them. #### Scenario: Candidate is saved - **WHEN** an operator saves valid bytes against the current active hash - **THEN** the candidate is durably staged with a new hash and the active file is unchanged #### Scenario: Active file changed through SSH - **WHEN** the active hash differs from the expected hash submitted by a stale page - **THEN** save or apply fails without replacing either active file #### Scenario: Candidate changed concurrently - **WHEN** the supplied candidate hash is no longer current - **THEN** apply is rejected before host lifecycle changes begin ### Requirement: Plaintext secrets editing without persistence leakage The protected secrets page SHALL allow authorized operators to view and edit the complete plaintext YAML document while responses remain no-store and secret values remain absent from browser persistence, application logs, diffs outside that page, operation status, and audit records. #### Scenario: Operator opens secrets page - **WHEN** an authenticated operator requests the dedicated secrets editor - **THEN** the current document is rendered in a server-side form over the protected no-store response #### Scenario: Secrets candidate is validated - **WHEN** an operator previews or saves changed secrets - **THEN** validation results name safe field paths and hashes but do not repeat credential values #### Scenario: Operator leaves the page - **WHEN** the browser navigates to another admin page - **THEN** the application has written no secret value to local storage, session storage, service workers, or client-side application state ### Requirement: Closed host-agent protocol The privileged host operations agent SHALL accept only a canonical operation UUID, one fixed action enum, expected active hashes, and expected candidate hashes from the authorized runtime peer over a local Unix socket. Deployment profile is root-installed host policy and SHALL NOT be added to that request. #### Scenario: Valid apply request arrives - **WHEN** the authorized runtime UID submits an exact valid request - **THEN** the agent verifies the persisted operation and hashes before acquiring the singleton apply lock #### Scenario: Request includes path or command data - **WHEN** a request includes a service name, path, shell text, environment, Docker argument, or unknown field - **THEN** the agent rejects it before any privileged action #### Scenario: Request attempts topology selection - **WHEN** a request includes a profile, ingress port, upstream, Caddy path, unit, service, command, environment, Compose file, or Compose argument - **THEN** the agent rejects it before reading candidate documents or stopping the deployment #### Scenario: Unauthorized local peer connects - **WHEN** a process with an unapproved peer identity uses the socket - **THEN** the agent rejects the request regardless of its JSON body ### Requirement: Coordinated apply with health verification For config, secrets, or combined apply, the host agent SHALL revalidate fixed candidate files, verify compare-and-swap hashes, create byte-identical backups, stop the fixed Truf deployment, atomically replace active files, recreate the exact root-selected runtime and edge profile, attest its fixed topology, and wait for required health before reporting success. In shared-host mode it SHALL NOT stop, restart, reload, reconfigure, or remove host Caddy, X-UI, or another unrelated service. #### Scenario: Combined apply succeeds - **WHEN** both candidates validate and the restarted deployment reaches Supervisor ACTIVE, PostgreSQL READY, Worker API, ingester, and projector health - **THEN** the operation completes successfully with resulting hashes and retained rollback evidence #### Scenario: Validation changes between preview and apply - **WHEN** host-side revalidation or hash verification differs from the accepted candidate operation - **THEN** the agent aborts before stopping the healthy runtime #### Scenario: Apply is requested while another is active - **WHEN** the singleton operation lock is held - **THEN** the second request is rejected or remains queued without overlapping lifecycle mutations #### Scenario: Shared-host profile is healthy - **WHEN** shared-host apply recreates a host-network runtime with no published ports and an edge bound only to fixed loopback, and all runtime and edge health checks pass - **THEN** the operation succeeds without a host-Caddy or X-UI lifecycle action #### Scenario: Shared-host topology drifts - **WHEN** runtime publishes a port, edge binds a non-loopback address, profile metadata changes, or Compose labels, mounts, network, capabilities, or Caddyfile differ from the fixed profile - **THEN** the agent rejects the deployment before candidate replacement or reports failed health without claiming success #### Scenario: Shared-host rollback succeeds - **WHEN** candidate health fails and the previous Truf runtime and edge are restored - **THEN** rollback completes exactly once without changing host Caddy, X-UI, or another unrelated service ### Requirement: Automatic rollback and failed hold If the new deployment fails its bounded health check, the agent SHALL restore byte-identical backups and verify the previous deployment; if rollback also fails, it SHALL stop retrying and retain a failed-hold state and all evidence for SSH recovery. #### Scenario: New configuration fails startup - **WHEN** the restarted runtime cannot reach required health within the deadline - **THEN** the agent restores the prior active files and restarts the previous deployment #### Scenario: Rollback succeeds - **WHEN** the restored deployment reaches required health - **THEN** the operation records rolled-back status and safe failure category without claiming apply success #### Scenario: Rollback fails - **WHEN** neither the candidate nor restored deployment becomes healthy - **THEN** the agent enters failed hold, performs no replacement loop or forced authority release, and preserves backups and diagnostics ### Requirement: Logical managed roots The generic file page SHALL address only configured logical roots with explicit read, create/replace, and delete permissions, and SHALL never accept an absolute root from a client. #### Scenario: Operator lists a managed runtime root - **WHEN** a valid logical root ID for logs, keycheck projections, or result projections and a canonical relative directory are requested - **THEN** the service returns a bounded read-only listing of permitted regular files and directories under that root #### Scenario: Operator downloads rotated result projections - **WHEN** the operator requests a permitted active or rotated result projection within its configured file-size bound - **THEN** the service returns that regular single-link file without granting mutation access or exposing the backing runtime path #### Scenario: Result projection names are allowlisted - **WHEN** the result-projection root is listed or read - **THEN** only active `scan_results.jsonl` and `found_secrets.jsonl` files and their exact six-digit generation names are visible, while locks, databases, ledgers, scan errors, temporary/quarantine directories, malformed generations, and recovery artifacts remain unavailable #### Scenario: Large result projection is downloaded - **WHEN** an allowed result projection is within the larger result-root byte bound - **THEN** the service copies and hashes an unchanged source revision into an anonymous same-volume snapshot using bounded chunks, permits only one such snapshot at a time, streams the snapshot with bounded memory, and releases the snapshot and concurrency slot after response completion or failure #### Scenario: Operator requests excluded storage - **WHEN** a request targets application code, config/secrets through the generic page, PostgreSQL storage, sockets, host-agent metadata, raw result bundles, or an unknown root - **THEN** the service rejects it without revealing host paths or existence details ### Requirement: Descriptor-safe path containment Managed-file traversal and mutation SHALL use a retained root directory descriptor, component-wise no-follow operations, canonical relative components, and regular single-link file checks. #### Scenario: Relative traversal is attempted - **WHEN** a path contains an empty, dot, dot-dot, absolute, drive-qualified, backslash, NUL, over-depth, or over-length component - **THEN** the request is rejected before filesystem access outside the retained root #### Scenario: Symlink is swapped during access - **WHEN** a path component becomes a symlink between validation and open - **THEN** no-follow descriptor traversal fails without accessing the link target #### Scenario: Hardlink or special file is targeted - **WHEN** the final object is multi-linked or is not a regular file - **THEN** view, download, replace, and delete are rejected ### Requirement: Durable bounded file mutation An allowed managed-file create or replace SHALL use an exclusive same-directory temporary regular file, bounded bytes, fsync, atomic descriptor-relative replacement, directory fsync, and final ownership/type/mode verification. #### Scenario: File replacement succeeds - **WHEN** an authorized bounded replacement is submitted against the current file hash - **THEN** readers observe either the complete old file or complete new file and audit records the safe before/after hashes #### Scenario: File is concurrently changed - **WHEN** the current file hash differs from the expected hash - **THEN** replacement fails without overwriting the concurrent change #### Scenario: Upload exceeds the root limit - **WHEN** submitted bytes exceed the configured bounded file size - **THEN** the service rejects and removes temporary data without changing the target ### Requirement: Content-free operational audit Configuration, secrets, and managed-file operations SHALL record actor, typed action, logical target, timestamps, result, safe category, byte counts where applicable, and before/after hashes, but SHALL NOT record file contents or credentials. #### Scenario: Managed file is deleted - **WHEN** an allowed delete succeeds against the expected hash - **THEN** audit records the logical root/path and previous hash without retaining deleted content #### Scenario: Secrets apply fails - **WHEN** a secrets operation fails validation, startup, or rollback - **THEN** status and audit expose only the bounded failure category and document hashes