# 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-content//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: workspace-content/example/evidence patterns: - "**/*.md" max_bytes: 10485760 policy: max_chunk_chars: 4000 retain_published_generations: 3 ``` Safe: `workspace-content/example/evidence`. Unsafe filesystem identities include `/srv/evidence`, `workspace-content/another/evidence`, and `workspace-content/example/../another/evidence` because absolute, cross-namespace, and traversal paths are not canonical. ## 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__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__EVIDENCE_ACCESS_KEY_FILE` and `THT_WS__EVIDENCE_SECRET_KEY_FILE` | Required together for `static_files`; each file is at most 65536 bytes. | | Static S3 session | `THT_WS__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`, ``, 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/ ├── workspaces/ │ ├── example.yaml │ └── another.yaml ├── workspace-content/ │ ├── example/evidence/... │ └── another/evidence/... └── workspace-docs/ ├── example/{contract.env.example,README.md} └── another/{contract.env.example,README.md} ``` Curators change only `workspace-content//evidence/**` through a normal clone. The API publishes only `workspaces/.yaml` and `workspace-docs//{contract.env.example,README.md}`. It never writes Evidence source bytes. ## Registry revision and phase ownership | Relationship | Contract | | --- | --- | | Revision identity | The 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 descriptor blob is unchanged. | | Browser | Create and edit flows preserve and show a read-only Evidence summary. | | Export | Export remains exactly manifest, descriptor, contract, and README; it excludes Evidence bytes. | | P1 | Validates the lexical URI and proves the declared filesystem root object is a Git tree at that same commit; it does not recursively inspect nested symlinks. | | P6 | Owns commit-addressed materialization, realpath and recursive containment, nested-symlink checks, and race checks. | P1 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 ``` 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