Tickets

Tickets

The issue-board app, walked end to end — one checked-in Rust serde schema encoded with CBOR, a set of pipeline routes, owner-scoped storage, and a GitOps deploy — as the worked example of the platform's narrow-app shape.

The tickets app is an issue board that runs on this platform and is exposed at /v1/tickets/* for managed preview and onboarding accounts. It is a real, deployed application rather than a sample: the same board drives the internal engineering queue and the managed preview surface, and every ticket and comment is a signed row in the engine like any other record. It is the clearest end-to-end example of the shape every narrow app on the platform follows — a checked-in Rust serde schema encoded with CBOR, a set of pipeline routes, transform code the executor runs, datasets in the engine, and a GitOps deploy — so it is the one to read first when building your own.

Status — preview/onboarding. The write endpoints (upsert, comment, claim, close, release) run on the pipeline path described here. The read endpoints (list, view, comments) are moving to the same path while preserving their public response contract.

The checked-in Rust serde schema is the ground truth. A ticket is a Ticket record and a comment is a TicketComment record, defined in the repository's schema crate and encoded on the wire and in storage with CBOR. Both carry a hierarchical primary key that gives every stored record a content-addressed identity. State and origin use typed enums, not free strings; the human-readable forms (open, github, and the rest) are produced downstream by the read pipeline, not stored. A ticket is event-sourced: each edit appends a new row keyed by the ticket id, and the latest row wins.

Pipeline manifests declare the app as routes. Each endpoint is one pipeline: an HTTP trigger plus a dataflow body. The writes (upsert, comment, claim, close) are short read-modify-write pipelines; release forwards the authenticated request to the engine so the owner-and-holder check and its audit row stay in one place. The reads are pure operator chains — run a published query spec, unwrap the rows, map the enums back to strings, then sort and paginate — with no compiled per-app code at all. Adding a route is writing one manifest, not editing a service.

The edge resolves identity and scopes the request. Every /v1/tickets/* route is authenticated for managed preview and onboarding accounts. These routes are absent from the generated public allowlist in the V1 route reference, so they are outside the self-service public API contract. A caller's sk-rg-... key resolves to a user id, and the platform forwards that verified identity as the request's owner, discarding any owner a client tries to supply. Every read is filtered to owner == caller and every write stamps owner = caller, so one caller reaching another's tickets is structurally impossible rather than a rule the handler has to remember.

The executor is where the app's logic runs. For a write, the executor decodes the request and dispatches on the route to the ticket write logic, which is shared code — the same functions back both the pipeline route and the older handler, so a write from either surface lands a byte-identical row. For a read, there is no per-app code: the executor runs the operator chain against a published query spec that scans the event-sourced dataset and collapses to the latest row per ticket id.

The engine stores the rows and enforces ownership. Every write is a single submit-transaction through the platform's one write chokepoint. The event-sourced rows live in the tickets and ticket_comments datasets; the latest-per-id read view is a projection over them. Row-level ownership is enforced by predicate pushdown in the storage layer, across both the columnar cold tier and the hot tier — the filter runs in the engine, not in application code.

Deploy is GitOps, generated, never hand-applied. Nothing here is kubectl apply-ed by hand. The CI pipeline generates the deployment manifests into a deploy repository, and ArgoCD syncs them to the cluster. The route manifests publish through the same CI spine on merge; a read route in staged rollout is promoted by removing it from the preview list once its output matches the reference exactly.

Observability rides the generic path. There is no tickets-specific dashboard. The app emits the standard service metrics and logs, which land in the platform's metrics and log stores; watching tickets in practice means querying those series for the engine and executor, not a bespoke exporter.

How this maps to the platform shape

Everything above is the same chain — Rust serde schema, CBOR, pipeline, route, executor, dataset, deploy — that every narrow app on this platform follows; tickets is the reference because all of it already exists and runs. To build your own app against this shape, the step-by-step walkthrough is Build a vertical, and the one-command scaffold that generates the whole structure from this same tickets pattern is Scaffold a narrow app.

Copyright © 2026