Any agent: read your field with a token
DIN is memory for any agent, not for one vendor's. Your field answers plain HTTP: an agent runtime that can send a GET request with an Authorization header can recall from it, search it and build a brief from it. It needs no plugin, no SDK and no MCP server. This page is the whole path: the requests, the token, the network rules the field enforces, a tool definition to paste into an agent framework, and what has been tested.
The requests below go to your own self-hosted field. Nothing here calls a model, and nothing leaves your machine unless you put a proxy in front, as described below.
What an agent can ask
| Ask | Request | What comes back |
|---|---|---|
| Recall | GET /api/recall?q=<words>&k=<1..25> | The best records for the question, newest first among equals, each with one snippet of at most 200 characters, its regime (how it was obtained) and an entry_hint address. no_confident_match says when nothing answers well. |
| Search | GET /api/search?q=<words> | Records whose words match, as id, title, snippet and facet. |
| The brief | GET /api/recall and GET /api/query?facet=all&q=<words> | The two calls din brief makes. The field serves no /api/brief: the brief is a layout of these rows, done by you or your agent. |
| Health | GET /healthz | Whether the field is up and how many records it holds. It needs no token. |
Every route the field serves is in the field's own API reference, API.md, which ships with the field's source. This page covers the three an agent needs first.
The token
Where it comes from
The installer mints the field token into ~/din/.din/field.token, readable only by you. It is 64 random hex characters, and no DIN program ever prints it. Give it to your agent runtime the way that runtime keeps secrets: an environment variable, a secret store or a vault. Never put it in a prompt, a URL or a file the agent can show to someone.
On the field's own machine:
export DIN_FIELD=http://127.0.0.1:8093
export DIN_TOKEN="$(cat ~/din/.din/field.token)"
8093 is the port of your main field. din field list shows the port of every field in this install.
What it opens
The field token opens the whole field: every read, the human stamp (POST /api/annotate), and switching the active field. There is no read-only token and no per-agent token for an agent today. Whoever holds it can read everything your field holds, so give it only to an agent you would let read all of that.
To narrow what an agent can do, narrow the network path to it: the proxy below passes only GET requests under /api/, so an agent that reaches the field through it can read and cannot stamp.
Rotating it
Write a new token into the file, then restart the field: din field stop, then din field start. The old token stops working at once. Give the new one to every agent that used the old one.
umask 077 && head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > ~/din/.din/field.token
Calling it with curl
Recall:
curl -sS --fail-with-body -H "Authorization: Bearer $DIN_TOKEN" "$DIN_FIELD/api/recall?q=flour+reorder+threshold&k=5"
Search:
curl -sS --fail-with-body -H "Authorization: Bearer $DIN_TOKEN" "$DIN_FIELD/api/search?q=flour+reorder"
The brief, its two calls:
curl -sS --fail-with-body -H "Authorization: Bearer $DIN_TOKEN" "$DIN_FIELD/api/recall?q=flour+reorder+threshold&k=12"
curl -sS --fail-with-body -H "Authorization: Bearer $DIN_TOKEN" "$DIN_FIELD/api/query?facet=all&q=flour+reorder+threshold&limit=60"
Sort the query's rows into three groups, each row counted once, the way din brief does:
- Contradicted:
stateisrefuted,failed,contradicted,disputedorabandoned, orflagshasrestated_after_refutationorhuman_refuted. - Decided: not contradicted, and the row is a decision (
facetisdecisions) or itsstateisproved,proven,tested,verified,confirmed,passed,succeeded,direct_outputorborne_out. - Unproven: neither, and the row is an open question (
facetisopen_questions) or itsstateisasserted,claimed,stated,unverified,not_run,inconclusive,partial,pending,openorunsupported.
A row that fits none of the three is ungraded: count it apart. Keep the contradicted and unproven rows. They are the point of the brief: a summary that drops them hides what your record argues with itself about.
Health, with no token:
curl -sS --fail-with-body "$DIN_FIELD/healthz"
A wrong token is refused with 401 and the body {"error":"missing or bad token"}:
curl -sS -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer not-the-field-token" "$DIN_FIELD/api/recall?q=flour"
A tool definition for an agent framework
Most agent frameworks take a tool as a name, a description and a JSON Schema for its arguments. Here are the three reads in that shape, as plain JSON. Map method, url and headers onto your framework's HTTP tool, put the token in from its secret store, and send the arguments as the query string.
{
"tools": [
{
"name": "din_recall",
"description": "Recall what this team's agents already decided, tested or refuted, from the DIN field. Each hit carries its regime (how it was obtained) and one short snippet. Treat no_confident_match: true as 'nothing on record'.",
"method": "GET",
"url": "${DIN_FIELD}/api/recall",
"headers": { "Authorization": "Bearer ${DIN_TOKEN}" },
"parameters": {
"type": "object",
"properties": {
"q": { "type": "string", "description": "The question, in plain words.", "examples": ["flour reorder threshold"] },
"k": { "type": "integer", "minimum": 1, "maximum": 25, "default": 8, "description": "How many hits to return." }
},
"required": ["q"]
}
},
{
"name": "din_search",
"description": "Find DIN records whose words match, by id, title and snippet.",
"method": "GET",
"url": "${DIN_FIELD}/api/search",
"headers": { "Authorization": "Bearer ${DIN_TOKEN}" },
"parameters": {
"type": "object",
"properties": {
"q": { "type": "string", "description": "Words to find.", "examples": ["flour reorder"] }
},
"required": ["q"]
}
},
{
"name": "din_brief_rows",
"description": "The rows behind a DIN brief: every claim, decision, open question and experiment that matches, each with its state and flags. Group them into decided, unproven and contradicted, and keep all three.",
"method": "GET",
"url": "${DIN_FIELD}/api/query",
"headers": { "Authorization": "Bearer ${DIN_TOKEN}" },
"parameters": {
"type": "object",
"properties": {
"q": { "type": "string", "description": "The question, in plain words.", "examples": ["flour reorder threshold"] },
"facet": { "type": "string", "enum": ["all"], "default": "all", "description": "Every facet." },
"limit": { "type": "integer", "minimum": 1, "maximum": 5000, "default": 60, "description": "How many rows to return." }
},
"required": ["q"]
}
}
]
}
The network rules the field enforces
These are rules in the field's code, not advice, and no setting turns any of them off.
- Loopback only. The field binds a loopback address and nothing else.
FIELD_BINDmust be127.0.0.1(or another127.xaddress),::1orlocalhost, with a port; the field refuses to start on any other. No setting opens it to the network. - A loopback Host only. Every request whose
Hostheader is not127.0.0.1,localhostor[::1](any port) is refused with403. That stops a web page from reaching your field through DNS rebinding. A request with noHostat all goes on to the token check. - A token on every
/api/request, asAuthorization: Bearer <token>. A token in the query string is refused on every route but the live event stream (/api/stream), so it never lands in a proxy log. The field reads its token from the environment only, refuses to start with one under 16 characters or with spaces or control characters around it, and warns under 32. - Reads are GET.
POSTis accepted on/api/annotate,/api/groundand/api/binding/activateonly. Every other method, and a POST anywhere else, is refused with405. - Plain HTTP. The field does not speak TLS itself. On the same machine that is fine: the traffic never leaves it.
Reaching it from another machine: TLS in front
An agent that runs somewhere else, in a cloud agent platform for example, needs HTTPS to your field. Pick the narrowest path that works:
- The agent on the same machine: use
http://127.0.0.1:<port>and stop here. - The agent on a machine you control: an SSH tunnel, as in Use DIN as a team, and the agent uses
http://127.0.0.1:<port>on its side. - An agent platform on the internet: a TLS reverse proxy on the field's machine. This is the only one of the three that puts your field on a public address, so allow only the platform's published addresses if it has them.
The proxy must send the field a loopback Host, because the field refuses any other. An nginx server block, for a field on port 8093:
server {
listen 443 ssl;
server_name din.example.com;
ssl_certificate /etc/letsencrypt/live/din.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/din.example.com/privkey.pem;
location /api/ {
limit_except GET { deny all; }
proxy_pass http://127.0.0.1:8093;
proxy_set_header Host 127.0.0.1:8093;
}
}
limit_except GET keeps the agent to reads, and only /api/ is passed, so the console page is not published. Your agent then uses https://din.example.com as DIN_FIELD, with the same token and the same requests. The din program does not use this path: it speaks plain http to a field, so it sends the token to a field off this machine only with DIN_ALLOW_REMOTE_FIELD=1.
What is proven
| Runtime or path | How it was exercised | Status |
|---|---|---|
| curl, on the field's own machine | tools/tests/any_agent_doc.sh reads every command block on this page and runs it against a real field on a loopback port, then checks the status codes and the response shapes, and that a wrong token is refused on every route. | Tested on Linux x86-64 |
| The tool definition above | The same test calls each tool as written: its URL, its header and its example arguments. | Tested as HTTP calls. No agent framework has loaded it. |
The proxy's Host line | The same test sends the field the Host the nginx block above sets, and the field answers. | The field's half is tested. nginx itself was not run. |
| A ChatGPT agent | Once, by hand, by the DIN team: a ChatGPT agent read a field through HTTPS, with a bearer token. No test repeats it. | Exercised once |
| Every other runtime | Not exercised. | Should work, not tested |
"Should work" means: the runtime can send an HTTPS GET with an Authorization header and read JSON. That is all this path asks of it.