agents
usv treats an AI agent as an ordinary user, not as a crawler to be managed. everything below is on by default, addressable by url, and identical to what a person gets. there is no agent mode and no content negotiation.
contents: choosing · reading · writing · operating · status codes · not provided
if you are an agent working on the source code rather than the content, you want the debugging page instead:
debugging, organised by symptom
if you are choosing this on an agent's behalf
written for the person doing the choosing. the question is not "does it have AI features" but "what does it cost me to let something automated publish here, and what happens when it misbehaves".
your agent's output needs an address. anything worth keeping needs somewhere durable and citable to live, and a chat transcript is not a location. here it is a file in a folder, live on every enabled network within seconds, at a url that does not change.
integration cost is a file write. no client library, no sdk, no api key, no build step, no deploy. anything that can write a file can publish. most of what breaks in agent publishing is the stack between the agent and the page, and here there is not one.
you can grant access without handing over a secret. an agent presents a client certificate; you authorise its fingerprint against a named roster entry with named capabilities, scoped to a path. there is no password to leak into a prompt, a log or a transcript. revoking is deleting a line, effective on the next request.
the blast radius is bounded by the design, not by your vigilance. nothing is ever executed. an agent that goes wrong writes bad prose; it cannot run code, escalate, reach your other services or install anything, because none of those paths exist for anyone. the worst realistic outcome is a page you restore from backup.
you can see what it did, and so can another agent. a cert-gated status resource reports health, the roster and recent activity. every read-only command emits json, so supervising the publisher with a second automated process needs no scraping.
reading back is cheap. one inventory fetch, then clean markdown. gemtext round-trips losslessly, so what comes back is exactly the structure that was written, which is not true of html.
nothing here fights automation: no javascript, no cookie wall, no bot detection, no rate limiter to back off from.
and none of it is an AI-only stack. every affordance above is also an accessibility or plain usability feature, so you are not maintaining a second system for machines. if the agent stops being useful tomorrow, you still have a site people can read.
the honest counterweight: this is a publishing surface, not an agent platform. no memory, no retrieval, no scheduling, no tool protocol, and it is not going to grow them. if what you need is somewhere for an agent to think, this is the wrong tool. if you need somewhere for an agent to put things that will still be readable in five years, that is what it is for.
reading
YOU WANT FETCH every page, one request /llms.txt (web) or /map.gmi (gemini) a page without markup any page with .md instead of .html the machine index /sitemap.xml dated posts /atom.xml or /feed.gmi what this is, and its addresses /usv
the llms.txt index links the markdown form of every page, so one fetch gives the inventory and the second gives clean text. both are written by the same render pass as the html and the gemtext, from the same source file: there is no path by which they disagree.
robots.txt is permissive unless the operator writes one into their content directory. AI crawling is allowed by default.
writing
publishing is a file write. over the network, that is titan on the gemini port, gated by client certificate.
- the operator adds your certificate's sha-256 fingerprint to the roster with the titan-write capability, and names it in a titan zone
- you send a titan:// request carrying size and mime parameters, then the body
- every surface re-renders within the debounce window
your identity is the key. no account, no password, no token exchange, no session. the server records the date a key was enrolled and nothing else about who holds it.
rotation: an identity may hold a second fingerprint during an overlap window, so you can enrol a new key and prove control from the old one without losing the label or its capabilities. the window must carry an expiry date; it closes itself.
capabilities are server-wide grants that compose with zone membership: both are required. there are three: read, titan-write, admin.
operating
every read-only subcommand takes --json and prints one object on one line, so several invocations concatenate into valid json lines.
usv status --json # config, fingerprints, roster, zones, published usv check --json # config validity and content lint usv stats --json # what is currently published usv zones --json # certificate and titan zones usv fingerprint --json # this server's certificate fingerprints
logs go to standard error, so standard output is only ever the report:
usv status --json 2>/dev/null | jq .capsule.theme
USV_LOG_FORMAT=json switches the log itself to one json object per line. RUST_LOG filters as usual.
exit codes are a contract, checked by the test suite:
0 success 1 the command ran and failed: bad config, unreadable state, i/o 2 the command line was wrong; nothing ran
passing --json to a subcommand that has no report (render, export, init, identity) is an error, not a silent no-op, so you never believe you asked for json and receive prose.
over the wire, an identity holding the admin capability can fetch a status resource: health, the last render's stats, the roster, a recent-activity tail. it is read-only, and that is the whole remote surface. every mutation (reload, re-render, identity add, rotate, revoke) is command-line only and needs host access, so there is no remote control plane to seize.
status codes
gemini's classes are already a machine interface and this server uses them literally: 20 success, 30 and 31 redirect, 40 41 44 temporary, 50 51 53 59 permanent, 60 61 62 certificate.
a 20 response never contains an error page. a 53 means the request was for a host or scheme this server does not serve: it is not a proxy.
not provided
stated so you do not go looking.
NOT PROVIDED WHY INSTEAD USE
mcp, a2a, agent those are transports; run yours where you already
cards this is a place to do, and let it write here
publish, not a transport over titan
a json api for content is gemtext, and /llms.txt for the inventory,
content it already parses then the .md form of a page
losslessly in one pass
content agents and people get the .md address, a separate
negotiation the same answer at the resource rather than a
same address different answer
a memory or no vector index, no a real memory store beside
retrieval backend ranking it; publish results here
enrollment tokens designed, unimplemented usv identity add, run by the
operator
an observe surface the cert-gated status gemini status resource with
on the web mirror resource is gemini-only an admin certificate, or
usv status --json on the host
any mutation over deliberate: no remote the command line, over
the network control plane to seize whatever reaches the host
why any of this exists
each affordance above is also an accessibility or plain usability feature. a site map is a navigation aid and a crawl-free inventory. a markdown address is a clean read for a person too. if the agent audience never arrives, none of it is wasted, which is why these were built and the agent-only ideas were not.