Files
netris-nestri/apps/api
Wanjohi bbe729e5c7 feat(api): the session endpoint, and a claim that only one caller can win
A run of a box had core support and no HTTP surface. This adds both halves
of it: a person asks for a run and reads it back, and the host agent the box
is placed on is handed the work and reports what happened.

The access rule is the point. An agent may only see or touch a run whose box
is placed on its own hardware, and that is a `where` clause on every one of
the three agent endpoints rather than a check next to them — host credentials
are long-lived secrets sitting on hardware in somebody's home, so what one
leaking can reach has to be decided by the query. "No such run" and "not your
run" are the same refusal, so ids cannot be discovered by reporting states
at them.

`Session.setState` updated on the id alone, which means two agents polling
the same work both succeed and both start the same box. There is one host
today, which is exactly why that would have been built wrong and stayed
wrong. The state a run is moving out of is now part of the `where` clause,
so the database picks the winner; the loser gets a conflict rather than a
silent no-op. Three cases that look alike are kept apart: re-reporting a
state you already reported changes nothing and is not an error, a transition
that does not exist is refused with the run left where it was, and another
host reporting anything is forbidden.

Asking for a run makes no decision about where it happens — a box already
names its hardware, so the run inherits it by join. Placement therefore
gets an interface at box creation, where the decision actually is, with the
single-host case as its implementation and a deliberate refusal when there
is more than one candidate and no policy to choose with.

Tests cover the wire shape from both sides, the query scoping, the claim,
and the timestamp idempotence a run's billing rests on.
2026-09-04 18:57:08 +03:00
..
2026-08-06 22:13:51 +03:00
2026-08-06 22:13:51 +03:00
2026-08-06 22:32:33 +03:00
2026-08-06 22:13:51 +03:00

apps/api

The public HTTP API for Nestri — a Hono app deployed as a Cloudflare Worker.

What it does

Exposes the JSON API consumed by frontends and other clients. Every route is a thin wrapper that validates input, delegates to a domain function in @nestri/core, and returns { data: ... }. All business logic lives in the core package.

Routes:

Prefix Purpose
/ Health check
/user Current user profile, fingerprints, linked accounts
/steam Link / sync / unlink a Steam account
/library Owned games with playtime
/games Game catalog
/pairing-code Device pairing codes
/machine Host machines
/access-token Short-lived access tokens
/doc Generated OpenAPI spec

Structure

app/
  index.ts           # Hono entrypoint: middleware, routes, error handler, /doc
  middleware/auth.ts # Bearer JWT + admin shared-secret auth → Actor
  routes/*.ts        # Thin route namespaces (UserApi, SteamApi, ...)
  utils/             # ErrorResponses, Result(), validator wrapping
test/                # Route tests (Vitest/Bun)

Key details

  • Auth: Authorization: Bearer <JWT> verified against @nestri/auth; or the x-nestri-admin-token header (see ADMIN_SHARED_SECRET).
  • Errors: centralized VisibleError → typed JSON responses.
  • The API worker receives its bindings (AUTH, HYPERDRIVE, STEAM_API_KEY, ADMIN_SHARED_SECRET) from Alchemy — see alchemy.run.ts at the repo root.

Running

Run via the root Alchemy setup (bun alchemy.run.ts --dev). Needs a Postgres database and an auth worker; see the root README for full dev setup.