Scope of every claim on this page
Statements below describe the Gate E1 local slice on a developer machine. Nothing here is a claim about a hosted service, because there is no hosted service. TWIRX is not approved for public multi-tenant arbitrary-URL deployment, and the requirements that would have to be met first are listed in full further down.
The adversary model
A system whose input is the public web has an adversary by default. TWIRX assumes an attacker may control any of the following, simultaneously, and that none of it is unusual.
The request path
Submitted URL text, DNS records and address changes, redirects, ports.
The response
HTTP headers and status, compressed and malformed bodies, resource-exhaustion attempts.
The content
JSON keys, values, nesting, duplicate semantic claims, and metadata that impersonates publisher authority.
The proposals
Adapter proposals, ontology and mapping proposals.
The model input
Prompt-injection content planted for future model-assisted stages. The corpus already contains such a vector.
The project itself
One ordinary contributor account.
The full model, including protected assets and per-threat residual risk, is in THREAT_MODEL.md.
Threats and current controls
Each row states what Gate E1 does and what it does not do about it. The right-hand column is the more useful one.
| Threat | Implemented control | Residual risk |
|---|---|---|
| Server-side request forgery | HTTP/HTTPS allowlist; no URL credentials; private and reserved address rejection; direct dialling of validated resolved addresses; redirect revalidation; standard ports by default; request timeout and body limits. | These are application-layer controls, not a physically separated egress network. Resolver and routing behaviour still need production hardening and adversarial testing. |
| Evidence substitution | SHA-256 body addressing; envelope body digest and size; re-hash before extraction; independent C verification; immutable path convention. | Local filesystem compromise can replace code and evidence together. Release signing and external transparency are not in Gate E1. |
| Parser and memory corruption | Memory-safe Go in the privileged path; a tiny bounded CBOR subset; a fixed C envelope schema; input size caps; dual-compiler builds; ASan and UBSan; shared Go/C vectors; Go mutation fuzzing; a sanitizer-backed C libFuzzer target; no C networking or plugin loading. | Parser defects remain possible. Finite tests and short fuzz runs cannot establish their absence — only continuous fuzzing and independent implementations reduce this. |
| Semantic injection | Provider text stays untrusted data; native term and value stay visible; mappings are explicit and versioned; adapters are manually admitted; no model exists in the canonical path; required fields fail closed. | A reviewed adapter can still encode a mistaken interpretation. Conformance and disagreement mechanisms have to grow before this is adequately addressed. |
| Resource exhaustion | Limits on URL, response, envelope, JSON bytes, nesting, scalars, containers, tokens, redirects, timeouts, and reads; duplicate keys and trailing top-level values rejected before extraction; no browser or model; fixed extraction operations. | Service quotas, concurrency budgets, and per-origin circuit breakers do not exist yet. |
| Treasury compromise | Only public addresses may enter the repository; seed phrases and private keys are forbidden; the project wallet is separated from personal wallets; ledger reconciliation against public chain records. | A single-founder wallet is a temporary concentration of authority during Genesis, and is disclosed as such on the funding page. |
Fail-closed parsing
The bounded JSON profile rejects, rather than repairs: more than one top-level value, duplicate object keys at any depth, escaped surrogate code units that do not form a valid pair, and any input past its declared byte, depth, scalar, container, or token limit. The deterministic CBOR profile rejects non-shortest integers, indefinite forms, wrong field and digest lengths, invalid UTF-8, U+0000, non-canonical UTC times, trailing bytes, and bodies over 2 MiB.
Rejecting a duplicate key rather than taking the last one is a small decision with a large consequence: last-key-wins hides source ambiguity and varies between implementations, which would let an attacker construct a document that two conforming systems read differently.
Candidate versus canonical authority
The security property the whole architecture is organised around is that producing output and being believed are separate privileges.
- Anything may produce a candidateAn origin, an adapter proposal, a browser worker, a model, a contributor. Candidates are data with provenance.
- Only the admission path creates canonical stateIn Genesis, that is a reviewed repository change containing fixtures, expected outputs, compatibility analysis, and provenance.
- No component can promote itselfNot by asserting authority in its output, not by being confident, and not by being the only implementation available.
The independent C verifier is a concrete instance of this: it validates the canonical artefact and has no network path, no registry authority, and no ability to write canonical state. It can say no. It cannot say yes on its own.
The production egress gap Not proven
This is the most important limitation on this page, and the first unresolved risk in the Gate E1 register.
The URL policy and resolved-address checks in internal/safefetch are
application-layer defences written for local development. They are careful — they
validate the resolved address rather than the hostname, dial it directly, and
revalidate on every redirect — but an application-layer check is not equivalent to
a network that cannot reach private ranges in the first place.
A production deployment that accepts arbitrary URLs from untrusted users needs network-level enforcement, because the failure mode of an application-layer control is a single missed code path. TWIRX therefore does not accept arbitrary URLs from untrusted users, and will not until the requirements below are met.
Loopback
The --allow-loopback flag deliberately weakens public network policy
so the controlled fixture can be observed during local development. It must never
become an option on a public service.
The public-deployment gate
Before arbitrary URLs may be exposed to untrusted public users, the project requires all of the following. This list is a commitment, published in SECURITY.md before there is any pressure to compromise on it.
- Separate worker and control networks.
- A dedicated egress proxy and DNS control.
- Process, container, or microVM isolation.
- No worker access to the control database, registry signing keys, cloud metadata, or control credentials.
- Per-tenant quotas and per-origin budgets.
- Audit logging and anomaly detection.
- Tested revocation and recovery.
- External security review.
Future isolation work Planned
Two later gates add the components with the largest attack surface the project will ever adopt. Both are constrained in advance:
Browser isolation
E4. Discovery only.
- Runs inside process or microVM isolation
- No access to canonical state or credentials
- No path into the execution hot path
- Output is a candidate, subject to admission
Model isolation
E5. Proposals only.
- Observed content is assumed to contain prompt injection
- Model output is untrusted data, never an instruction
- No model may write canonical state or authorise an action
- Every accepted proposal keeps the record that accepted it
Residual risks
The seven open risks against Gate E1, unedited. They are also on the proof page and in project-status.json.
- URL and resolved-address checks are application-layer controls. Production deployment still needs separate worker and control networks, DNS and egress enforcement, metadata isolation, and quotas.
- Observation v1 records the request and final URL but not the redirect chain or selected transport headers. A later immutable transport-evidence artifact is needed without mutating v1.
- The adapter runtime has no independent second implementation. Its manifest and result formats remain pre-stable and unsigned.
- Atomic publication is per file, not a transaction spanning every file in an observation bundle. Destination directories and CAS roots are assumed to be trusted configuration, and filesystem durability semantics vary.
- The corpus is intentionally small and the fuzz durations in normal tests are smoke tests. Longer scheduled fuzzing and external review remain necessary.
- Direct command-package coverage is absent even though the shell workflow exercises their current happy path. CLI error-path tests should be added at the next maintenance gate.
- Local filesystem compromise can replace implementation and evidence together. Signed releases and external transparency are out of Gate 1.
Report a vulnerability
Do not disclose a suspected vulnerability in a public issue, discussion, social post, or pull request.
Once the repository is public, private vulnerability reporting will be enabled and the repository's Security tab is the route. Until that channel is configured, keep the report private and contact the maintainer through an existing trusted private channel.
A useful report includes
- the affected commit or release;
- the component and the trust boundary involved;
- reproduction steps or a fixture;
- expected and observed behaviour;
- potential impact and blast radius;
- whether credentials, canonical state, or cross-origin access are involved;
- any proposed mitigation.
Do not test against third parties
Use the controlled test origin and the local adversarial fixtures. Testing this project's behaviour against websites you do not own is not authorised by anything here, and the project will not normalise it.