Security model
Touchstone is deliberately an HTTP cannon. It sends malformed, hostile and high-volume requests to the servers it tests, it handles credentials, it can be driven by an AI agent, and it runs clients’ own code to drive them. Six rules keep that safe. They are not negotiable, and the code enforces each one.
- 1. Pre-registered targets only
- 2. Redact on the server side
- 3. Responses from the server under test are untrusted
- 4. People approve tests
- 5. The MCP endpoint is local unless you secure it
- 6. A driven client runs only what its operator registered
- Credentials in practice
1. Pre-registered targets only
Rule: Touchstone tests only servers listed in its target registry, and every interface accepts a target id, never a URL.
- The CLI’s
--target, the MCP tools’targetId, and the run engine all resolve ids throughtargets.yaml. An unknown id is refused. - The GitHub Action takes the target URL as an input, and writes the registry itself. The value reaches the script through an environment variable, never by expression interpolation. It must be an absolute
http(s)URL without whitespace or quotes, and it is written withprintf, so a crafted input cannot add targets or run shell commands. - The Docker image runs as an unprivileged user.
2. Redact on the server side
Rule: credentials are removed before a trace exists, so no report, file or tool response can leak one.
- Authorization, cookie, DPoP and API-key headers are replaced with
[REDACTED]. - OAuth, OpenID Connect and token-exchange parameters (
access_token,id_token,subject_token,client_secret,password, and others) are blanked wherever they appear: as a JSON member, a form field or a query parameter. - Bodies are truncated after 2,048 characters.
Name-based redaction cannot catch a secret under a non-standard name, which is one more reason bodies are truncated. Reports and verdicts has the full list.
3. Responses from the server under test are untrusted
Rule: anything the server under test sends is data, never instructions or configuration.
- Every MCP trace is labelled as untrusted server output, because agents read it, and a response body is a place to hide a prompt injection. Bodies in traces are truncated.
- JSON-LD contexts are never fetched. A context IRI in a response cannot make the harness send a request, or change the term mappings a verdict is computed from. Contexts Touchstone understands are bundled; any other one fails the assertion.
- Redirects are not followed, so a response cannot send the harness to another host.
4. People approve tests
Rule: a test enters the suite only through review.
The mapping from tests to requirements is what every verdict rests on. An agent can draft a test, and the MCP draft_test prompt helps it do so. The draft must still pass the definition checks and a run against the reference deployment, and then go through a pull request. No tool writes to definitions/.
5. The MCP endpoint is local unless you secure it
Rule: the MCP server listens on 127.0.0.1 by default.
Its tools are unauthenticated and can start runs. To host it, set server.address explicitly, and put an authenticating layer in front of it. The design calls for a Spring Security OAuth2 resource server; that does not exist yet.
6. A driven client runs only what its operator registered
Touchstone’s client driver (touchstone drive, and the MCP server’s driving tools) starts a client’s adapter, which is code the client’s developers wrote. It runs only the adapters adapters.yaml registers, with their command line, directory and environment, on the operator’s machine: the CLI and the tools take an id from the registry, never a command, and nothing a rule, a tool argument or an adapter says is ever run. It uses only the client-session services the registry names, and drives name only the session’s URLs and those the client reported from its answers.
- The driving tools are local. They exist only when the MCP server runs on stdio; the hosted endpoint never lists them.
- An adapter’s output is untrusted. Every line it writes is checked against the protocol’s schema, and is bounded in size and time; an adapter that breaks the protocol is stopped, and that is never a finding about the client. What the tools return from an adapter is labelled as data, not instructions.
- The session key stays in the driver. It is never logged, put in a drive’s variables or given to an adapter; the read-only client tools read a driven session by its id.
Credentials in practice
- Never put a real token in
targets.yaml. It is checked in, and it ships with the harness. UseTOUCHSTONE_TOKEN_<NAME>environment variables, such as CI secrets. - Files that ship with the harness describe mechanisms, not deployments. Specific identity providers, CID URLs and OAuth clients belong in the operator’s environment.
- Git ignores
runs/. Treat report bundles from real servers as sensitive even after redaction.