octoturge c72bfe71d6 Show the picture in the real web console, in a real browser
Every earlier camera proof stood in for the browser with a werift peer.
The question that decides whether the feature works is whether a person
looking at the fleet page sees a picture, and the web console had never
been opened.

BROWSER=1 on scripts/e2e-camera.sh now starts the real Nuxt app and
drives it in real Chrome (puppeteer-core): sign in, open the fleet page,
wait for the camera player, and check the result. Measured against the
H2C: the app's own Supabase client signs in, the header reports Live
(the telemetry socket authenticated with the real token), the player
reports connected, Chrome decodes 1680x1080 frames, playback time
advances, the frame has picture content (mean luma 88, stdev 62.8), and
the browser's own ICE stats show the selected candidate is a TURN relay.
No uncaught page errors.

The login page only offers a magic link by email, which a script cannot
follow, so the driver signs in with the app's $supabase.client and a
password just set on a throwaway user. That means AUTH=supabase: the
backend verifies against the real project's JWKS rather than the local
dev issuer, with e2e-supabase-user.ts creating or reusing
continuum-e2e@example.invalid (a reserved .invalid address) and
resetting its password each run. The JSON it prints carries a password
and a live token, so the shell captures it and never echoes it.

The frame is judged by pixel statistics and no screenshot is kept: it is
the inside of someone's workshop, and "is there a picture" does not need
one stored. The relay is read from getStats() through a wrapped
RTCPeerConnection rather than inferred from configuration. A browser run
with fewer than its nine results fails, so a driver that dies quietly
cannot read as a pass.

puppeteer-core is a devDependency for these scripts alone. Its 22
resolved packages are all Apache-2.0, BSD-3-Clause, ISC or MIT.
2026-10-24 11:47:24 +00:00
2026-10-20 00:44:35 +00:00

continuum-backend

Cloud control plane for Continuum. Bun + ElysiaJS, PostgreSQL (with pgvector for frame embeddings), Valkey Streams, and Cloudflare R2 storage.

Run

bun install
scripts/dev-env.sh up        # Valkey + Postgres/pgvector, and print the URLs
scripts/dev-env.sh schema    # apply drizzle/ - same as bun run db:migrate
cp .env.example .env         # fill in the URLs it printed, plus R2_*, SUPABASE_JWT_SECRET
bun run dev

scripts/dev-env.sh uses docker-compose.yml when a Docker daemon is answering, and falls back to the same two services from apt when there is no usable container runtime - some sandboxes cannot run Docker at all, because dockerd needs CAP_NET_ADMIN for its bridge and user namespaces to unpack an image, and neither can be granted from inside. scripts/dev-env.sh status says which backend is in use.

Schema changes go through migrations: edit src/db/schema/, run bun run db:generate, and commit the file it writes to drizzle/ along with the schema change. scripts/dev-env.sh push reshapes a local database straight from the schema with no migration file, which is faster while a table is still moving but records nothing and will drop a column to make things match - so generate before committing. drizzle/0000_*.sql creates the vector extension itself; that line is hand-added, since drizzle-kit does not emit extension statements and the migration would otherwise only apply to a database somebody had already prepared.

Test

bun test                 # unit + protocol contract tests, no services needed
scripts/e2e-edge.sh      # the whole edge path: real database, real printers

scripts/e2e-edge.sh seeds a farm, claims its printers, mints an edge token, runs the real continuum-proxy against this backend, and asserts that live telemetry reached Postgres and that a command reached a printer. It only ever sends a fan speed, and only to a printer reporting IDLE. scripts/capture-gateway-frames.js is the smaller tool: it stands in for this backend and records what a gateway sends, for refreshing the fixtures in tests/fixtures/.

Layout

  • src/db/ - Drizzle schema (printers, farms, print_jobs, frame_embeddings) and client, pointed straight at Postgres. src/db/validators.ts derives Elysia/TypeBox request validators straight from the Drizzle tables via drizzle-typebox, so the Postgres schema and the API validators come from one definition instead of two hand-synced ones.
  • src/s3/ - R2 presigned URL helpers (PUT for client uploads, GET for the edge proxy).
  • src/queue/ - Valkey client (ioredis) + XADD helper pushing onto stream:camera_frames for continuum-ai-worker.

Valkey, not Redis: Redis 7.4+ is RSALv2/SSPL and 8.0+ adds AGPLv3, none of which a commercial product can ship. Valkey is BSD-3-Clause and speaks the same RESP protocol, so ioredis is unchanged - only the server differs. See continuum-common/docs/LICENSES.md. docker-compose.yml here brings up Valkey and Postgres+pgvector for local development.

  • src/routes/ - /auth, /farms, /printers, /jobs, and the edge-gateway WebSocket at /ws/edge/v1. That route's wire format is not defined here: it is schemas/continuum/v1/edge/ in continuum-schemas, validated through its TypeBox mirror in @continuum/ts-types, because both ends of it are separate programs in different languages and they had already drifted apart once. printers.gateway_printer_id is what maps a gateway's own printer ids ("p1p") onto rows here; telemetry for an unclaimed id is logged and skipped, never inserted.

frame_embeddings.embedding requires the vector extension. The first migration creates it, and scripts/dev-env.sh up also does so for the push path, which does not run migrations.

Dependency pins

elysia, @sinclair/typebox and the drizzle-* packages are pinned tightly on purpose, not out of caution. Caret ranges here resolved into a combination that could not boot: elysia: ^1.1.26 installed 1.4.30, which calls t.Module and so needs TypeBox >=0.34, while drizzle-typebox: ^0.2.1 installed a version whose peers demand drizzle-orm >=0.36 against the ^0.33.0 pinned here. Typechecking passed throughout - only starting the server failed. tests/dependency_identity.test.ts guards the part of this that can silently come back.

S
Description
Continuum platform: continuum-backend
Readme
624 KiB
Languages
TypeScript 63.8%
Shell 35.4%
JavaScript 0.8%