import RUN --root ROOT: a setup-only release tool on the served repository, offered as a POST on the page; dry runs check and list changes without writes. Import uses the owner's settings/builders.json, counts the run and first reports through Acting's receipt rules, compares the stage pattern, checks shared bytes and inventory, copies and smokes before offering next, then removes shared files. Stage, import, accept and rollback share one root lock. |
spec/ocx/release_import_spec.cr, spec/ocx/dry_run_spec.cr, spec/ocx/styles_spec.cr |
The release root: staged/<check-in>, staged/next and staged/gate.log are the agent's, the log holding all that a stage's gate printed, written as it runs, so a long gate can be followed, and a gate that fails refused with its last line, the first five examples that failed with how many in all, and the log; current, previous and accepted copies are the server's; a release holds the shard.yml targets built from tools/ and nothing else in bin/, with gate.txt and source.txt (the checkout's remote, without its login, from which the page links each tool to the line that declares it at the release's own check-in) |
spec/ocx/staging_spec.cr, spec/ocx/release_commands_spec.cr, spec/guards/provenance_spec.cr, spec/ocx/styles_spec.cr, spec/ocx/command_spec.cr |
The CGI: reads FOSSIL_REPOSITORY, FOSSIL_USER, FOSSIL_CAPABILITIES, FOSSIL_NONCE, FOSSIL_URI, REQUEST_METHOD, QUERY_STRING, PATH_INFO, CONTENT_LENGTH, HTTPS, HTTP_HOST and HTTP_REFERER by value, the build settings by value, and only the names of the rest of the environment; served as ocx2-cgi, the active release candidate, or ocx2-cgi-next, the pre-release candidate, saying which, inside Fossil's skin, or JSON with json; answered as Markdown that Fossil renders in its skin; a bar at the top linking each group of tools and each tool; with no tool named, the OCX page, /ext/ocx2-cgi: its header linking console, a page of its own of which it draws no card, then its tools in groups, drawing nothing a tool does not answer (runs, an agent's work, the repository and its node, releases, about this program), each group's title heading its tools, each tool a card: what it does, a "(src)" link to the file and line that declare it in the release's source at its own check-in, the capabilities it needs marked held or not for the viewer, what a changing tool would do now (its dry run, an operation a line), and its form, a POST, so nothing typed in it is kept in a URL or cut short with one, which is its input (dry_run among its switches, json the page's own, each described in its tooltip), left out when a POST of it would be refused: the viewer lacks what it needs, or the form has nothing for the viewer to fill in and what the tool would do now stops (a plan made with no input cannot judge a form with fields, such as create's); with a tool named, a button back to the list, the tool's answer and its own card, which beside a dry run leaves out what the tool would do now; the context's facts in a strip, as set by Fossil and the server, and a release root refused from the request; a style block taking its colours from the skin; stage not offered, saying why; a tool that writes nothing, whatever it needs, taken from any request, a GET among them, the viewer still holding what it needs; one that may change something taken only by a POST whose Referer is OCX's own page, Fossil's base URL then /ext/ocx2-cgi or /ext/ocx2-cgi-next then nothing, / or ? (the base https when Fossil's HTTPS is on, as fossil server --https sets it behind a proxy that ends TLS, whatever REQUEST_SCHEME says), since Fossil passes no Sec-Fetch-Site and no CSRF token, and never from another page of the site, such as a file a user committed, which Fossil serves under /doc (HISTORY.md decision 111), a GET of it showing its dry run; an answer's result before its log; a leaf that links by its words on one line, where it leads percent-escaped; console's own page, below; text escaped as Markdown, attributes as HTML |
spec/guards/environment_spec.cr, spec/ocx/styles_spec.cr, spec/ocx/elements_spec.cr, spec/ocx/console_spec.cr, spec/ocx/harness_spec.cr, spec/scenarios/question_spec.cr |
What waits on a user, as data: questions [--for USER], needing 2r, writing nothing, the same answer from the page, as JSON, its host's command line, MCP, or a clone's checkout, where the server's receipt tool says who sent each question and decision; through the one generic layout, as every tool's answer; {for, waiting, to_confirm, open}: each question {asked, where, title, link, url, from, at, for, question, options, stakes, run, replies_to, references, decisions, confirmers}, each field once, absent ones left out, link from the repository's root by the first ten digits of a hash, at in UTC as its writer's clock said, replies_to and each of references described {title, kind, by, at, excerpt, link, url} as far as the caller may read it, each decision {from, at, note}, a note's hashes shown by their titles; no post's text, nor its block beside the question it holds |
spec/ocx/questions_spec.cr, spec/guards/question_spec.cr, spec/scenarios/question_spec.cr |
A person's view: console [--for USER] [--login], needing 2r, writing nothing, from the page, its host's command line, MCP, or a clone's checkout. In a clone's checkout it shows runs only given --login; elsewhere it shows the record's runs. It reads questions' answer and the runs as status reads them, and each entry point lays it out its own way. Its page and its text are laid out for a person and are no interface of data: the page, /ext/ocx2-cgi/console, a page of its own in the skin with no list of tools, no answer around it and no form of its own, one link to the list, its title then one block of HTML, <html>, which Fossil's Markdown keeps whole, each link in it under the repository's root as FOSSIL_URI names it, each press a button posting its input and back to its tool, after which the page answers a redirect to console?written=HASH, saying so in a line, or console's page saying why nothing was written; the text, at the command line and over MCP, with no answer around it, the presses of a question as one call, the command line after the program and --repository and --user as given, or over MCP ocx_post and its input as JSON, the field to fill in as TEXT, then each option by its label, each excerpt's lines quoted, a control character shown as U+FFFD; on both, chosen by the tool's name, a section holding nothing left out and named in one closing line, and a refusal or a dry run one line, then in the text next: where the tool says what to do instead, a dry run's line saying it was one and, where console would stop without it, why. Its JSON is the view's data, which this row pins: the answer, its result {for, waiting, to_confirm, open, clone_users, running, ended}, the questions shaped for reading and the runs: clone_users, an array naming every node user an administrator's run names, empty when none is known; running and ended give their runs; local care is read only with --login on a clone; each question {question, for, from, at, where, changes, read_first, decisions, decide, confirm}, read_first only for what waits on you, its references, absent when it has none, decide and confirm only in your own view, holding what post needs, each a press {label, recommended, tool, input, asks, hint} of post --decision, its question named by the first ten digits of its hash, its input as post reads it, decision true, which MCP's ocx_post takes as it is; each run {objective, where, sent_by, runs_as, phase, since, note}, phase one of waiting, starting, running, stopping, lost and ended; at and since how long ago, and in JSON the time in UTC; no hash shown but in a link or a press, nor in its log, which counts the artifacts it asks the server of. |
spec/ocx/console_spec.cr, spec/ocx/styles_spec.cr, spec/scenarios/question_spec.cr |
release [--root ROOT] [--release NAME --file FILE]: current and previous, names and SHA-256 inventories; listed file bytes as base64, current or previous only, signed-in setup or owner-listed builders only, nothing written; USAGE holds the fields |
spec/ocx/release_read_spec.cr |
Git exports: the program served under the name git, or git-next from the release offered as next (ocx2-git and ocx2-git-next too), (<ext>/git/NAME.git/…), serves the git export of the repository Fossil serves the request through, NAME its file's name without its extension, read-only over git's smart HTTP (info/refs?service=git-upload-pack and git-upload-pack, a gzipped request accepted, one whose gzip does not read refused with 400; a request body over 50 MB, raw or expanded, refused with 413, and one announced over it never read), from the directory its last-git-export-repo setting names, which fossil git export records (Fossil's export hook, installed by hook, and export keep it current); a push is refused (403), a viewer without o or h gets 403, another repository's export, whatever its file is named, 404, since Fossil gives the viewer's capabilities for the served repository alone; a repository without an export 404, an export git cannot read or a fetch git fails 500, git's answer read whole before any header is written; git is started only by OCX::Git, with PATH alone in its environment |
spec/ocx/git_export_spec.cr |
The git export kept current: export, setup, on the served repository named outright, refused in a checkout and withheld from MCP, runs fossil git export into the directory the repository's last-git-export-repo setting names (refused when it names no git repository), one run at a time: a run first leaves ocx-export-again in the export's .git, then tries the flock on ocx-export-lock there, and the holder exports while that mark is left; the run is kept as ocx-export-last.json there, {"when", "took_ms", "runs", "added", "failure"}, added what Fossil said each export added; a failed export refuses with what Fossil said, and the next run exports what awaits; the output is the run and the state fossil git status gives (mirror, awaiting, last_export, autopush, main_branch) with the last run, and a dry run lists the export with awaiting, autopush and the last failure; where Fossil cannot say, as after an export killed before it kept anything, awaiting is absent, unreadable says why, and the export runs and mends it; nothing sets an autopush |
spec/ocx/export_spec.cr, spec/ocx/dry_run_spec.cr |
project NAME [--daemon-root DAEMON_ROOT] [--from UPSTREAM] [--mirrors DIR] [--kinds "session"]: make a served repository empty or from git, export it, join the login group, install its hook, chat user and ticket fields, with --daemon-root, file its NAME-node and run users' passwords privately under that root's settings/credentials/, for the running daemon to make its clone in state/clones/ within a minute; on the page it runs detached from the page's release root |
spec/ocx/import_spec.cr, spec/ocx/imports_detached_spec.cr |
An imported repository brought up to its upstream: what the upstream added since, from the export's marks, onto each branch Fossil has not moved past and onto upstream/BRANCH where it has |
pending: slice 39 |
Onboarding a repository: the hook installed, email-url from the served repository's, the person onboarding signing in by Fossil's login group, the web page in its menu |
pending: slice 37 |
A run's user: a Fossil user made for runs by a person, in Fossil's user pages, or by create, which gives it gio24 and R; it holds neither admin nor setup and is not a node user, or the node refuses the run; the node's credentials file supplies its password for a password step, used to clone and remembered by the run's clone; the watcher resets no server password |
spec/ocx/watcher_spec.cr, spec/ocx/decisions_spec.cr, spec/ocx/create_spec.cr |
A tool run at the command line with no repository named takes the repository of the checkout it is run in, found as Fossil finds it, by its checkout file (.fslckout, or the older _FOSSIL_) from the working directory up, read directly; one named with --repository always wins, and outside a checkout there is none. A repository named that is not there is said to be, and no fossil command runs on it but the clone that makes it, so none leaves a lock beside it. A tool that acts on the served repository takes a checkout's only where Fossil's export hook is installed in its repository, which marks the served one, since a hook's settings are never cloned; elsewhere it refuses, naming the checkout's repository (the maintainer, 2026-10-07, after the use-fslckout branch's example) |
spec/ocx/command_spec.cr, spec/ocx/hook_spec.cr, spec/ocx/fossil_spec.cr |
Given --receipt-fd N, the web page's receipt relays to the receipt service: the launcher has sent its identity line {user?, capabilities, repository}, then the CGI sends {ocx_receipt: 1, artifacts?: [...], capabilities_of?: USER} and reads the service's answer, with no repository access; this sandbox refuses every other tool. Who sent an artifact, for protocol v1 (docs/slices-v1.md, slice v1.4): receipt [--artifacts 'HASH ...'] [--capabilities-of USER], needing no capability, on the served repository named outright; in a clone's checkout, the server's own receipt asked, unsigned, its refusal given as it says it, and its answer refused unless it is of the artifacts asked, in order, and the user asked; in any other checkout, refused; each hash in full (SHA1 or SHA3-256, in hex), any other refused; for each, in the order asked, whether the served repository received it (a phantom it has only heard of is not received; asked of 500 at a time, so any number may be), the login its receipt names, that login's capabilities as Fossil grants them, inherited ones included, the receipt's number (rcvid) and the artifact's row number (rid), which orders the artifacts of one receipt; for --capabilities-of, that user's capabilities, wiki_moderated, whether the repository holds each wiki edit they save for a moderator (its modreq-wiki on, and the user lacking ModWiki, l), and ticket_moderated, whether it holds each ticket change they make in its ticket pages (its modreq-tkt on, and the user lacking ModTkt, q), all absent for one it lacks; to a caller without Read (o), and for a private artifact to one without x, every artifact not received and no capabilities; never the receipt's address |
spec/ocx/receipt_spec.cr, spec/ocx/receipt_service_spec.cr |
The check-ins no verdict has judged, by the reading rules: unaudited [--branch BRANCH], needing no capability, refused on the served repository to a caller without Read (o); the public check-ins with a row in Fossil's mlink (a check-in changing files), on BRANCH by its branch tag or on every branch, oldest first by receipt (rcvid) then row (rid), at most 50, each {checkin, branch, user, comment}, user its U card, with count; less every one on which a control artifact's T card (+ or *, events of type g, never only tagxref) sets audited or sym-audited to a value matching pass: … (against …) or fail: … (against …), received from a user who holds i when read, is no login named as the node of a run an admin or setup sent, and neither sent the check-in nor is its U card's user, and whose X, when a hash in full, names a run about the check-in: one whose first started from its node names it as checkout, or whose checkins names its branch, a hash naming no run counting for nothing; from a clone's checkout, the server's own answer to an unsigned POST of json and, given one, branch to /ext/ocx2-cgi/unaudited, answered in full within ten seconds, with asked the server's URL |
spec/ocx/verdict_spec.cr, spec/scenarios/audit_spec.cr |
What was said where a record is, for protocol v1: thread OF [--after POST], needing Read (o), not offered on the web page; OF the hash, or a prefix of at least 4 hex digits, of any post of a forum thread, of a ticket's id or of any change of it, as post TO and create --ticket also name a ticket, refused when it names neither or more than one; each post of the thread (forumpost rows whose froot is its first post, edits included) or each change of the ticket that sets icomment, in the order received (rcvid, then rid), as {hash, from, when, text}, when the post's fmtime or the change's tkt_mtime in UTC as RFC 3339 writes it; at most 20, after the one POST names, refused when POST names one the server has not received, with more counting the rest; in a clone's checkout it refuses a clone with no remote and a pull that reports Error: or makes no round trip, pulls, reads the clone, and orders and names each sender by the server's receipt answers, asked unsigned 64 at a time, one the server has not received last and from no one, refused when the server says it has not received the first post or change, which no tool writes in a clone, as where its user nobody lacks Read (o); on the served repository named outright, its own receipts, a private artifact (private), such as a post awaiting moderation, left out for a caller without x |
spec/ocx/thread_spec.cr |
A node on a clone signs in as the node user in its credentials file, holding goC4w, neither admin nor setup, and unmoderated ticket writing. Each daemon start restores the clone's default user and names its saved remote ocx-server. Reports are posted through /forume2 or /tktedit; verdicts are pushed with --once ocx-server and confirmed through the receipt page, and shares use uv sync ocx-server when the user holds y. Neither changes the saved default remote. Credentials name each run user's password explicitly; no served reader sets one. |
spec/ocx/daemon_spec.cr, spec/ocx/daemon_setup_spec.cr, spec/ocx/run_pages_spec.cr, spec/ocx/server_pages_spec.cr |
A login asked for and given (HISTORY.md decisions 73 and 76): register SERVER CREDENTIALS [--login LOGIN] [--capabilities LETTERS], from the asker's command line alone, needing no capability there, the one tool needing none that changes something, which the web page and MCP withhold; with no file CREDENTIALS it makes a 32-byte random code and keeps {"user", "code", "capabilities"} there (by default goC4w), readable by its owner alone; with a code, it POSTs login and code to collect, the first call too, and keeps the password in place of the code, or refuses while the login is not approved with the link SERVER/ext/ocx2-cgi/approve?login=LOGIN&key=SHA-256&capabilities=LETTERS, to be opened only as printed, its capabilities those the file keeps (a file {"user", "code"} an earlier release made: those given, or goC4w), refusing other capabilities given; with a password, it says so. approve LOGIN KEY CAPABILITIES, needing admin, on the served repository, not over MCP: refuses a login that is not lowercase letters, digits and dashes, one of Fossil's own users, a key that is not 64 hex digits, no capability, a, s, u or v, a letter Fossil 2.27 does not define but R, a user holding admin or setup, and a user approve did not make or that no longer holds the password it set; makes the user holding exactly CAPABILITIES with a random password, or sets the one it made, keeping the password for the same key, and keeps {"key", "password"} in the config row ocx-node-key-LOGIN. collect LOGIN CODE, needing no capability and changing nothing, not over MCP: the password when the code's SHA-256 is the key kept and the user still holds that password, else refused |
spec/ocx/logins_spec.cr, spec/ocx/dry_run_spec.cr |
Letting a user send runs (HISTORY.md decision 109): send_runs USER [--off], needing admin, on the served repository named outright, at every entry point; refuses, naming why, one of Fossil's own users (nobody, anonymous, reader, developer), whose capabilities OCX never changes, and a user the served repository lacks; reads the letters the user holds of its own (its user.cap), then adds R at the end of them unless they hold it, or with --off takes every R out, in one SQL statement that leaves every other letter as it is then, so one another writer gave the user after that read stays, and that writes nothing where the letters already say what is asked (HISTORY.md decision 111); answers {user, capabilities, note}, the capabilities as receipt reads them, inherited ones included, and note only when the user holds admin or setup, saying R changes nothing it may do, or when, R taken away, it holds R still from Fossil's own users, saying so; on the web page a GET, as the link send_runs?user=USER, shows its dry run with the form holding USER, and only a POST whose Referer is OCX's own page changes anything |
spec/ocx/send_runs_spec.cr, spec/ocx/dry_run_spec.cr |
The one program: ocx2 chooses its entry point by the name it is started under (ocx2, ocx2-cgi, ocx2-mcp, git), and ocx2 --mcp is the MCP server too, as a program's own configuration starts it, and ocx2 --mcp-http HOST:PORT the same over HTTP on loopback alone (one message posted to /mcp answered as JSON, a notification 202, any other method 405, any other path 404, more than 10 MiB 413, another host refused, the MCP version a client asks for given when it is one spoken), and ocx2 --mcp-tunnel TUNNEL-ID the same through OpenAI's Secure MCP Tunnel (its key in OCX_TUNNEL_KEY, refused without one; each command long-polled from /v1/tunnels/ID/poll answered at /v1/tunnels/ID/response with its request_id, channel and shard token: a message jsonrpc_response, a notification notify_ack, a session's end session_termination_response, a command of an unknown kind or past its response_timeout not answered; a refused poll backed off, an answer refused for a time tried again; ended, saying so, when the key is refused), and ocx2 --mcp-ollama URL --model NAME --steps N --system TEXT MCP's caller instead, OCX's agent for local models as a run's command (below); in a clone, MCP's caller holds what the server grants its user, as the server's receipt says unsigned, not what the clone lists, setup, the command line keeping the clone's, its tools/list giving the tools MCP offers; each tool is defined once by its typed input and answer and reached from every entry point, refused by the caller's capabilities; a field's name or text that is not UTF-8 or holds a NUL, JSON's escapes decoded, is refused, naming it, before any tool reads it, at every entry point: the command line's own flags, its tool's name and an option's among them, the page, with a 403, MCP and OCX's agent's calls, which go on serving; create's fields and a pattern's JSON block are refused as the node refuses the run they make (above), naming its field; a failed operation's refusal keeps only its error's first line, so no answer carries a backtrace; every answer is {ok, tool, dry_run, log, result, stopped, next_step, took_ms}, its log the typed operations performed, the result only when the tool ran to the end, and --json prints it; as text, at the command line and in MCP's content, the result, or why it stopped, comes before the log, so a person reads what the tool answered first; a dry run is the same answer, changing nothing; with no tool it runs list, each tool with the derived schema of its input, which MCP's tools/list renders; help adds the output, context and answer schemas; status shows the context and names the environment present but unread |
spec/ocx/command_spec.cr, spec/ocx/elements_spec.cr, spec/ocx/dry_run_spec.cr, spec/ocx/styles_spec.cr, spec/ocx/mcp_http_spec.cr |
OCX's agent for local models, as a run's command: ocx2 --mcp-ollama URL --model NAME --steps N --system TEXT, URL http or https with a host and each setting --NAME VALUE in any order, any other command line a usage error (exit 2); its run's line, {"run", "objective"}, the first line of its standard input, and a delivery's line the second when there is one, a line that does not read refused (exit 1); the run read from the clone by its hash, refused (exit 1) when the clone holds none, when its process.keep.stdout_when is not always, or when it is a session whose deliveries is feed; Ollama's /api/chat asked by POST, nothing streamed: {model, messages, tools, stream: false}, the system's words then the objective and the delivery's line as the user's, each tool of the checkout's group as {type: "function", function: {name: "ocx_NAME", description, parameters}}, the parameters its input's schema; its reply {message: {role, content, tool_calls: [{function: {name, arguments}}]}, prompt_eval_count, eval_count}, a call written as text in content (each outermost JSON object with a name and arguments) taken as one, a call to a tool not offered refused, never run, and each answer sent with the next step as {role: "tool", content: its text, tool_name}; no request larger than the run's process.keep.stdout sent; each reply printed as one JSON line {sent, received} before any call it asks for runs, the request sent and the reply received each exactly, sent on the first alone, and each answer a JSON line {name, arguments, text, refused} after it, never past the run's process.keep.stdout in all; a call run only with room left for its answer, an answer that would pass it cut to fit, ending in [cut: the run keeps no more of its standard output]; once the model calls no tool, its last words posted as the run's user with post: a reply to the delivery's post, or on its ticket, else where the run is, exit 0; the step budget, the bytes, a refused post, or Ollama not reached, answering other than 2xx or no chat reply, or not in a step's time when one is given, said on standard error, exit 1; no read timeout, the run's wall its only clock |
spec/ocx/mcp_ollama_spec.cr, spec/battery/battery_spec.cr |