← Receivers

ui:navigate

ui family

Move the CALLER'S OWN reader to a same-origin path. Two independent checks, because one of them runs in code an untrusted caller can reach: (1) the SERVER addresses only the session that made this call — authority comes off the attested ctx, there is NO field naming a principal — `viewer`, `viewerId`, `actorId`, `actor`, `target`, `targetViewer`, `targetActor`, `session`, `sessionId`, `userId`, `uid`, `ownerSlug`, `audience` — and a payload carrying any of them is REFUSED outright rather than silently stripped (`workspace`/`slug` are NOT refused: they scope, they cannot name a reader); (2) the BROWSER independently drops any command that did not arrive on a stream it opened itself, so a command that somehow escaped (1) still moves nobody. Same-origin PATHS only — an absolute URL, a scheme, or a protocol-relative href is refused, never rewritten.

Effect
ask
Awaits an outcome — the call returns the response below.
Caller
session
The class of authority the caller must already hold, decided from the attested context with no round trip.
Cost
free
What one call costs, so you can budget before acting.
Reversible
yes
The effect can be undone by a later call.
Idempotent
no
Not safe to blind-retry — dedupe on the envelope idempotencyKey.

Send it with your agent

One click hands your coding agent a prompt that registers the substrate, reads this contract, and makes the call. Launch opens the app; the others copy the prompt.

Claude Code
Codex
Cursor
Gemini CLI
Claude Desktop
ChatGPT
curl -X POST https://one.ie/api/ask/ui:navigate \
  -H "Authorization: Bearer $ONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"data": { "href": "/u/one/in", "reason": "agent" }}'

The key is never in a link. npx -y @oneie/cli login writes it to ~/.config/oneie/key on your machine.

Request

Validated before dispatch — an invalid payload is refused with the fix, never half-applied.

  • href string required Where to. A SAME-ORIGIN PATH: one leading '/', e.g. '/u/one/in'. Refused, never sanitised: an absolute URL ('https://evil.com/x'), any scheme ('javascript:', 'data:'), a protocol-relative href ('//evil.com'), the backslash form ('/\evil.com'), a bare host ('evil.com/x'), and any href containing a tab/CR/LF. Measured against node's WHATWG parser: new URL('//evil.com', 'https://one.ie'), new URL('/\evil.com', …) and new URL('/<TAB>/evil.com', …) ALL resolve to https://evil.com/ — the parser strips tab/CR/LF anywhere in the input, so the tab form is a real bypass of a naive startsWith('/') check. A navigate receiver that accepts arbitrary hrefs is an open redirect with an agent attached.
  • reason select | command | agent optional Why the move happened, for the host's own judgment. Mirrors NavigateRequest.reason in one.ie/web/src/lib/navigate.ts, and is ECHOED back on the response so the transport has something to forward. CALLER-ASSERTED and carrying NO authority: an agent is free to send 'select', which reads as 'an operator clicked'. Nothing gates on it, and a host must not read it as evidence that a human was there.
  • replace boolean optional Replace the current history entry instead of pushing a new one. Default false: collapsing the entry deletes the operator's way back, so it is opt-in.

Response

What comes back from the call.

  • ok boolean
  • href string The href as accepted — byte-identical to the request, never a rewritten one
  • replace boolean
  • reason select | command | agent The request's `reason`, echoed unchanged. Caller-asserted — never evidence of a human
  • audience string The caller's OWN attested slug — echoed so a caller can see whose reader it addressed. Never a value the caller supplied
  • delivered boolean Whether the command actually reached a browser. FALSE today for every call: the receiver validates and addresses, and the transport that carries it to the reader is a separate row (task:01a0a3789956c0b2d78d2487). A receiver that reached nobody must say so rather than answer a bare ok:true — the route:to-* namespace is the standing example of what the other choice costs
  • note string
  • error string

Traffic

Every call to ui:navigate, counted where it is dispatched — over HTTP or in-process alike. Aggregate only — no actor, no payload, no workspace.

Counting…

Wiring

Every place in the open source that names ui:navigate, and the file that answers it. Read from the tree at build time — a receiver is reached by NAME through one door, so there is no import edge to follow and a grep is the honest shape of the question. Structure, not volume — the count is in Traffic above.

Called from

API route 1
  • one.ie/web/src/lib/event-vocabulary.ts:382

Answered by

ui:navigate one.ie/web/src/lib/resolvers/ui.ts:181 Dispatched through POST /api/ask/ui:navigate, after the envelope validates the payload.
JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "href": {
      "type": "string",
      "minLength": 1,
      "description": "Where to. A SAME-ORIGIN PATH: one leading '/', e.g. '/u/one/in'. Refused, never sanitised: an absolute URL ('https://evil.com/x'), any scheme ('javascript:', 'data:'), a protocol-relative href ('//evil.com'), the backslash form ('/\\evil.com'), a bare host ('evil.com/x'), and any href containing a tab/CR/LF. Measured against node's WHATWG parser: new URL('//evil.com', 'https://one.ie'), new URL('/\\evil.com', …) and new URL('/<TAB>/evil.com', …) ALL resolve to https://evil.com/ — the parser strips tab/CR/LF anywhere in the input, so the tab form is a real bypass of a naive startsWith('/') check. A navigate receiver that accepts arbitrary hrefs is an open redirect with an agent attached."
    },
    "reason": {
      "description": "Why the move happened, for the host's own judgment. Mirrors NavigateRequest.reason in one.ie/web/src/lib/navigate.ts, and is ECHOED back on the response so the transport has something to forward. CALLER-ASSERTED and carrying NO authority: an agent is free to send 'select', which reads as 'an operator clicked'. Nothing gates on it, and a host must not read it as evidence that a human was there.",
      "type": "string",
      "enum": [
        "select",
        "command",
        "agent"
      ]
    },
    "replace": {
      "description": "Replace the current history entry instead of pushing a new one. Default false: collapsing the entry deletes the operator's way back, so it is opt-in.",
      "type": "boolean"
    }
  },
  "required": [
    "href"
  ],
  "description": "There is deliberately no field naming WHO to move. The reader moved is always the caller's own, resolved from the attested session server-side. If you find yourself wanting a target, that is the defect arriving."
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "href": {
      "description": "The href as accepted — byte-identical to the request, never a rewritten one",
      "type": "string"
    },
    "replace": {
      "type": "boolean"
    },
    "reason": {
      "description": "The request's `reason`, echoed unchanged. Caller-asserted — never evidence of a human",
      "type": "string",
      "enum": [
        "select",
        "command",
        "agent"
      ]
    },
    "audience": {
      "description": "The caller's OWN attested slug — echoed so a caller can see whose reader it addressed. Never a value the caller supplied",
      "type": "string"
    },
    "delivered": {
      "description": "Whether the command actually reached a browser. FALSE today for every call: the receiver validates and addresses, and the transport that carries it to the reader is a separate row (task:01a0a3789956c0b2d78d2487). A receiver that reached nobody must say so rather than answer a bare ok:true — the route:to-* namespace is the standing example of what the other choice costs",
      "type": "boolean"
    },
    "note": {
      "type": "string"
    },
    "error": {
      "type": "string"
    }
  },
  "required": [
    "ok"
  ]
}