docs: define read-only workspace secret architecture

This commit is contained in:
2026-08-14 16:09:55 +02:00
parent f8117e8428
commit 870af3422b
@@ -0,0 +1,189 @@
# Read-only Workspace Repository and Runtime Secrets Design
**Date:** 2026-08-14
**Status:** Approved
## Purpose
ThothII consumes workspaces from one administrator-configured Git repository. Workspace authors
prepare and publish source outside ThothII. The application fetches, validates, and activates
repository revisions, but never edits, commits, pushes, imports, or exports workspace source.
Runtime credentials are intentionally absent from Git. After a workspace has been read, ThothII
derives the required credentials from its connector and authentication choices and lets an
authorized user complete them in the web application. The values are encrypted and persisted by
the backend; the browser retains neither workspace content nor secrets.
## Ownership boundaries
### Workspace source
The workspace source is an ordinary directory maintained outside the ThothII runtime. It contains
the catalog, each `workspace.yaml`, curated evidence, annotations, and other repository-owned
content. Authors validate it using source-side tooling and publish it through their normal Git
workflow to GitHub, GitLab, Gitea, or another standards-compatible server.
### ThothII installation
The installation descriptor selects the Git remote, branch, and one read-only authentication
transport. SSH uses a read-only deploy key plus pinned known hosts. HTTPS uses a read-only deploy
token and may provide a private CA. Secret values remain outside versioned configuration.
The installer performs a sanitized `git ls-remote` preflight. Credentials embedded in a remote URL
are rejected. The API exposes only a normalized repository identity: host, repository path, branch,
transport, active commit, and synchronization state.
### ThothII runtime
The local Git checkout, candidate validation area, immutable snapshots, and active state are
application-owned. They are read-only from the workspace-management API. A pull fetches a candidate
revision, validates the complete repository, and atomically activates it only if valid. A failed
candidate never replaces the last valid active revision.
ThothII never generates or reconciles files back into the checkout and never invokes Git commit or
push. Generated operational artifacts live under application data, not in the source repository.
## Repository synchronization states
A repository refresh has these states:
- `syncing`: fetching and validating a candidate revision;
- `active`: the candidate passed validation and became the active immutable revision;
- `invalid_candidate`: Git succeeded but repository validation failed; the previous revision stays active;
- `unavailable`: Git or authentication failed; the previous revision stays active;
- `empty`: no valid revision has ever been activated.
Validation is atomic at repository-commit level. A malformed catalog, descriptor, evidence tree, or
cross-file reference rejects the complete candidate revision.
## Runtime secret model
### Requirement discovery
The workspace descriptor contains connector type, authentication method, and non-secret logical
configuration. It never contains secret values or host filesystem paths. Connector adapters define
the secret fields required by each supported authentication method. For example:
- PostgreSQL `username_password` requires `username` and `password`;
- REST `bearer` requires `api_key`;
- SSH tunnel authentication requires the connector password and SSH private key;
- Evidence HTTP signed URLs and static S3 credentials contribute their own secret requirements.
Requirements have stable identifiers scoped by workspace and connector. Labels, descriptions,
input kinds, and required/optional status come from trusted application code rather than repository
HTML or executable metadata.
### Persistent encrypted store
The backend owns a `WorkspaceSecretStore` abstraction. The first implementation is a local encrypted
vault in application-managed persistent storage. Each secret is encrypted with authenticated
encryption and bound to its installation, workspace, connector, and field identifier as associated
data. Plaintext values never appear in Git, API responses, logs, error messages, diagnostics, or
browser storage.
The installation bootstraps one vault key independently from workspace content. Deployment tooling
owns its platform-specific provisioning; the workspace schema and GUI never contain filesystem
paths. The storage interface allows a future Vault, cloud secret manager, or OS keychain provider
without changing workspace descriptors or API consumers.
When an existing file-oriented harness connector needs a credential, the backend materializes it as
a restrictive temporary file in an application-owned runtime directory. Its lifetime is tied to the
diagnostic or runtime lease and it is removed on release. Persistent storage contains ciphertext
only.
### Secret API
For a selected workspace the API returns requirement metadata and status only:
```json
{
"workspaceId": "psd-clinical",
"state": "configuration_required",
"requirements": [
{
"id": "dwh.password",
"connector": "dwh",
"label": "Database password",
"input": "password",
"required": true,
"configured": false
}
]
}
```
A write request contains values only for the selected requirement identifiers. The response returns
status, never values. A delete operation forgets a configured value. Authorization is deliberately
deferred; the current authenticated application user may manage runtime workspace secrets.
Workspace readiness is derived as follows:
- `invalid`: repository structure or descriptor is invalid;
- `configuration_required`: structurally valid but required runtime values are missing;
- `ready`: required values exist but connectivity has not yet passed or is stale;
- `verified`: the most recent connector diagnostic passed for the active revision and current secret generation.
Changing or deleting a secret invalidates the previous diagnostic result.
## Browser behavior
Workspace management is a two-level read-only interface occupying at least 60 percent of viewport
width and height.
Level 1 explains the source/runtime separation and displays:
- normalized repository host and path;
- configured branch and read-only transport;
- active revision and last synchronization result;
- `Update workspace repository`, which fetches, validates, and conditionally activates a revision;
- the workspace list, with selection required for workspace-specific actions.
There is no Import bundle, Export bundle, Create, Edit, Delete, Publish, or conflict-resolution
operation. There are no browser-persisted workspace drafts or preferences.
Level 2 for the selected workspace explains and displays:
- immutable source identity and validation result;
- required runtime configuration grouped by connector;
- secret-entry controls whose values are write-only;
- `Save secrets`, `Forget` per configured value, and `Test workspace connection`;
- clear consequences for each button and a reminder that source changes must be committed and pushed
by an author outside ThothII before repository update.
The browser keeps form values only in component memory and clears them after submission or dialog
close. It never receives saved secret values.
## Compatibility and migration
Existing Git author settings, publish endpoints, bundle endpoints, generated-document
reconciliation, bootstrap catalog slots, and browser draft storage are removed. Existing environment
bindings may be read during a bounded migration period only to seed non-secret connector values;
secret file paths are not part of the new public workspace contract.
Session manifests continue to pin an immutable validated workspace revision. An already running
session keeps its acquired runtime lease; new or resumed work resolves the current encrypted secret
generation and fails closed when required credentials are unavailable.
## Failure handling and security
- Repository and vault errors use stable sanitized codes and never echo remotes with user info,
credential paths, secret identifiers that are not safe to disclose, or secret values.
- Vault writes are atomic and authenticated; corrupted ciphertext fails closed.
- Secret comparison uses no read API. Updating a secret is always a blind replacement.
- The backend applies request-size and field-count limits and rejects unknown requirement IDs.
- Temporary plaintext files use restrictive permissions, trusted directories, no-follow opens, and
deterministic cleanup.
- Git credentials are installation-only, read-only, and never sent to the frontend.
## Verification
Backend tests cover repository read-only behavior, atomic candidate activation, remote sanitization,
vault encryption and corruption, requirement discovery, blind secret writes/deletes, materialization
cleanup, readiness transitions, and absence of publish/bundle routes.
Frontend tests cover the two-level explanation, viewport dimensions, repository identity, selection
gating, dynamic secret forms, write-only behavior, status changes, and absence of local-storage,
import, export, editing, and publishing controls.
Deployment and CLI tests cover required remote/branch configuration, one read-only Git transport,
sanitized remote preflight, vault-key provisioning, and removal of Git author/write configuration.