Files
2026-09-30 20:30:56 +03:00

12 KiB

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
  • WHEN a path component becomes a symlink between validation and open
  • THEN no-follow descriptor traversal fails without accessing the link target
  • 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