docs: define read-only workspace secret architecture
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user