The HTTP front end and the graph as data
oto serve --http [host:]portputs the engine behind a threaded stdlib http.server on the same store: POST /rpc takes the JSON-RPC messages stdio takes and engine.respond answers both, so the two transports cannot disagree and a test proves it byte for byte; GET /api/<tool> answers one tool from its query string with the text and, where the store has them, the rows behind it; GET /api/graph is the whole graph as data, built by serve/payload.py through the store interface, so it holds nothing an agent could not be told and both stores produce it identically (the equivalence battery compares it); /api/status and /api/changes say what is served and whether it moved, for an app that polls.
With --view <name|dir> an app ontology is served at /, which is how a web app reads the graph live; the site build stage writes the same payload and the same app into build/site/ for static hosting. No route writes.
Beyond one machine, the rules are few and firm. The server binds to localhost unless a host is given, and any other host requires OTO_SERVE_TOKEN in the environment or the server refuses to start, saying so.
With a token, every call must carry it as Authorization: Bearer <token>; a browser that opens the app carries it once, as /?oto_token=<token>, and gets an HttpOnly cookie and a redirect to the plain address, so the token never sits in the page's URL.
The token is compared in constant time, never written to a file, and replaced in the server's log. Cross-origin calls are refused unless --cors <origin> names the origin (* for any), for a hosted app calling a separate server, and then the preflight allows the bearer header.
Neo4j credentials stay in the server process. In repository mode the deploy workflow publishes the static site beside the store when the repository variable OTO_SITE_VIEW names a view.