Files
ThothII/docs/contracts/workspace-evidence-v3.md
T

194 lines
8.9 KiB
Markdown

# Workspace Evidence v3 contract
This is the canonical public contract for the optional `evidence` object in a schema-v3
workspace descriptor. Evidence is optional: a valid v3 descriptor without it remains operational.
When present, `evidence` is strict: it contains `source` and a defaulted strict `policy`; every
source variant and the policy reject unknown keys.
## Filesystem source
A filesystem source uses the exact URI `<workspace.id>/evidence`. `patterns` is a nonempty list of
unique, normalized relative POSIX globs. Its defaults are `patterns: ["**/*.md"]` and
`max_bytes: 10485760`.
### Example: filesystem
```yaml
evidence:
source:
type: filesystem
uri: example/evidence
patterns:
- "**/*.md"
max_bytes: 10485760
policy:
max_chunk_chars: 4000
retain_published_generations: 3
```
Safe: `example/evidence`. Unsafe filesystem identities include `/srv/evidence`,
`another/evidence` and `example/evidence/../another` because cross-namespace and traversal
paths are not canonical. Legacy split-layout paths are rejected as noncanonical.
## HTTP source
`uris` is a nonempty, unique list of canonical HTTP or HTTPS provenance identities. Each URI must
have no whitespace, control character, backslash, userinfo, query, or fragment. Public mode uses
`authentication: none`; signed mode uses `authentication: signed_urls_file`. Defaults are
`authentication: none`, `connect_timeout_ms: 5000`, `read_timeout_ms: 30000`,
`max_bytes: 10485760`, `max_redirects: 5`, `allow_private_hosts: false`, and
`max_cache_bytes: 67108864`. Public HTTP (`authentication: none`) uses the declared query-free
URIs directly and requires no Evidence credential file. Signed HTTP uses the installation file
contract below and preserves a mandatory one-to-one provenance mapping in declared-URI order.
### Example: http
```yaml
evidence:
source:
type: http
uris:
- https://evidence.example.invalid/report.md
authentication: signed_urls_file
connect_timeout_ms: 5000
read_timeout_ms: 30000
max_bytes: 10485760
max_redirects: 5
allow_private_hosts: false
max_cache_bytes: 67108864
policy:
max_chunk_chars: 4000
retain_published_generations: 3
```
The shown provenance URI is safe and query-free. An HTTP fragment identity such as
`https://evidence.example.invalid/report.md#section` is unsafe. Query-bearing and userinfo-bearing
HTTP identities are rejected; public documentation must not spell or publish a signed transport
URL.
## S3 source
`uri` is a canonical `s3://` identity with a valid bucket and no port, userinfo, query, or fragment.
`endpoint_url`, when present, is an origin-only HTTP(S) URL; `region`, when present, is nonblank.
Defaults are `credentials: ambient`, `trusted_endpoint: false`, `allow_private_endpoint: false`,
`allow_insecure_endpoint: false`, `max_bytes: 10485760`, `max_objects: 10000`, `max_pages: 100`,
and `page_size: 1000` (and page size cannot exceed 1000). A custom endpoint requires
`trusted_endpoint: true`; an HTTP endpoint additionally requires `allow_insecure_endpoint: true`.
Endpoint-policy flags cannot be enabled without `endpoint_url`. Ambient S3
(`credentials: ambient`) uses the runtime provider chain and requires no Evidence credential file.
Static mode uses `credentials: static_files`, requires access-key and secret-key files together,
and permits an optional session-token file.
### Example: s3
```yaml
evidence:
source:
type: s3
uri: s3://example-evidence/curated/
credentials: static_files
trusted_endpoint: false
allow_private_endpoint: false
allow_insecure_endpoint: false
max_bytes: 10485760
max_objects: 10000
max_pages: 100
page_size: 1000
policy:
max_chunk_chars: 4000
retain_published_generations: 3
```
Safe: `s3://example-evidence/curated/`. Unsafe identities include
`s3://Invalid_Bucket/evidence` and `s3://example-evidence/evidence#section`. S3 userinfo and query
identities are rejected in prose and implementation; no credential-bearing example is published.
## Policy and numeric domains
The strict policy defaults to `max_chunk_chars: 4000` and
`retain_published_generations: 3`. Filesystem `max_bytes`; HTTP `connect_timeout_ms`,
`read_timeout_ms`, `max_bytes`, and `max_cache_bytes`; S3 `max_bytes`, `max_objects`, `max_pages`,
and `page_size`; and both policy values must be positive safe integers from 1 through
9007199254740991. HTTP `max_redirects` must be a nonnegative safe integer from 0 through
9007199254740991. S3 `page_size` has the stricter maximum of 1000.
## Installation files
The namespace is the workspace ID uppercased with every `-` changed to `_`. All Evidence variables
hold file paths, never credential or signed-URL values.
| Mode | Variable | File contract |
| --- | --- | --- |
| Signed HTTP | `THT_WS_<NAMESPACE>_EVIDENCE_SIGNED_URLS_FILE` | Required for `signed_urls_file`; at most 1048576 bytes; nonempty UTF-8 JSON string array in declared-URI order; query-stripped identities must match `uris` one-to-one. |
| Static S3 pair | `THT_WS_<NAMESPACE>_EVIDENCE_ACCESS_KEY_FILE` and `THT_WS_<NAMESPACE>_EVIDENCE_SECRET_KEY_FILE` | Required together for `static_files`; each file is at most 65536 bytes. |
| Static S3 session | `THT_WS_<NAMESPACE>_EVIDENCE_SESSION_TOKEN_FILE` | Optional, valid only with the required access/secret pair, and at most 65536 bytes. |
Every variable is an absolute path to a readable regular file whose resolved target is strictly below one of the roots configured by `THT_WORKSPACE_SECRET_ROOTS`. Scalar S3 files are nonempty
UTF-8 tokens without whitespace or NUL. Public docs, exports, and rendered YAML never expose file contents. `changeme`, `replace-me`, `YOUR_SECRET`, `<secret>`, access-key-looking strings, and any
credential-bearing or query-bearing URI are forbidden as public placeholder values.
## One shared registry repository
All workspace namespaces live in one Git repository:
```text
registry.git/
├── thoth-workspaces.yaml
├── example/
│ ├── workspace.yaml
│ └── evidence/...
├── another/
│ └── workspace.yaml
└── workspace-docs/
├── example/{contract.env.example,README.md}
└── another/{contract.env.example,README.md}
```
The curator-owned root catalog `thoth-workspaces.yaml` uses the `schema_version` value `1` and the ordered
`workspaces` list of `{id, name, description?}` entries. It is authoritative for workspace ID,
name, description, and display order. The descriptor at `<id>/workspace.yaml` must match the
catalog metadata exactly. `workspace-docs` is the reserved top-level API directory and cannot be a
workspace ID.
Catalog-only entries without `<id>/workspace.yaml` are valid bootstrap slots and surface as
`configuration_required`. The API may create `<id>/workspace.yaml` only when the catalog slot
already exists and no Git object exists at that path in the exact pulled base commit. After
bootstrap, existing descriptors change only through curator Git commit/push and installation pull.
The API never writes `thoth-workspaces.yaml` or `<id>/evidence/**`. Generated docs stay outside the
workspace namespace at `workspace-docs/<id>/{contract.env.example,README.md}`. An explicit docs
synchronization may create a docs-only commit that changes only `workspace-docs/**` and preserves
the catalog, descriptor, and Evidence object IDs.
## Registry revision and phase ownership
| Relationship | Contract |
| --- | --- |
| Revision identity | The catalog blob, descriptor blob, and filesystem Evidence root tree are checked at the same 40-hex Git commit. |
| Content-only revision | An Evidence-only commit changes authoritative `revision.commit` even when the catalog and descriptor blobs are unchanged. |
| Docs-only sync commit | A docs-only synchronization may advance `revision.commit`, change only `workspace-docs/**`, and preserve the catalog, descriptor, and Evidence object IDs. |
| Browser | Read-only curated workspaces preserve and show a read-only Evidence summary; only a catalog-only bootstrap slot may draft the first descriptor. |
| Export | Export remains exactly manifest, descriptor, contract, and README; it excludes Evidence bytes. |
| P1.1 | Validates the lexical URI `<id>/evidence` and proves the declared filesystem root object is a Git tree at that same commit; it does not recursively inspect nested symlinks. Evidence materialization stays out of scope for P1.1. |
| P6 | Owns commit-addressed materialization, realpath and recursive containment, nested-symlink checks, and race checks. |
P1.1 performs no acquisition, extraction, preprocessing/indexing, embeddings, Qdrant writes,
`ACTIVE` publication, retention, or GC.
## Operator validation
After the runtime configuration is rendered or acquired, validate it with the exact per-command
option ordering:
```sh
tht config check -c <path>
```
Stop after validation. P2/P6 later owns preprocessing and materialization.
## Acceptance states
These gates are independent and are not implied by this documentation contract.
automated integration: PENDING
manual acceptance: PENDING