Crew integrity for live sports and entertainment production.
The game went long. Wrap slipped two hours. Twenty-two people in that compound have somewhere to be tomorrow, in eleven different cities — and some of them are going to a different network's show.
Apron is an irregular operations desk for live production travel. It does not book your original travel; that's a solved problem. It watches the show, the itinerary, and everything that impacts whether the plan holds — then keeps every next call intact.
Named for the apron — the ground where aircraft turn, and the stage that reaches past the curtain.
Built for Agentic Cinema: The Blockbuster Hackathon. Partner track: Grafana — see Partner & Google Cloud usage.
- The problem
- Quick start
- The agent crew
- Agent separation — the security model
- Provenance and disclosure
- Partner & Google Cloud usage
- Observability — the Grafana pipeline
- What's here, and what isn't
- Repository layout
- Contributing
- License
Original booking works. Your travel management company does it well, weeks out, against a schedule everyone agreed to.
The failure is in the four hours after a live event decides to run long, and the person holding it is an on-site production coordinator standing in a truck compound with a phone and a spreadsheet. Three things break:
- An uncovered position. The A1 misses Monday's call. You backfill with a local who has never mixed that booth.
- A turnaround violation you find out about later. IATSE and NABET-CWA measure turnaround wrap-to-call, and travel time counts as work, not rest. A generic travel tool will happily book a flight that satisfies the schedule and breaks the contract.
- The 3:00 AM phone tree. Twenty-two crew, eleven destinations, one after-hours desk.
Unlike team travel, the crew disperses. There's no single group itinerary to fix — there are twenty-two individual ones.
Runs entirely offline against synthetic fixtures. No TMC, no carrier credentials, and no live game feed required to see the full scenario.
git clone https://github.com/markmcspadden/apron.git
cd apron/apron-starter
pnpm install
pnpm demo # replays the ALCS Gm 4 twelve-inning nightThen open http://localhost:3000/demo for the watch board. The scenario auto-plays when a client connects. Use the seat switch in the header to move between the TMC desk and the production seat and watch the same incident change shape depending on who is allowed to see what. The marketing site is at http://localhost:3000/.
pnpm tape # 22-crew fixture on port 3002The tape fixture is purpose-built for the demo video: 22 traveling crew (not 14),
game starts at Bot 10th already in extras, all four external calls remain NOT
BROKERABLE so the production seat shows four External call · withheld rows at
the seat switch. Same game, same chain, same agents — a tighter cut that fills
every beat of the three-minute narration.
cp .env.example .env # add credentials + partner keys
pnpm dev # full server on :3000 — Firestore-backedDev mode starts the full server with Firestore persistence, the admin console at
/admin, the live board at /board, agent dashboards at /traffic,
/wrangler, /spotter, /steward, and the demo fixture at /demo.
Every variable is documented in .env.example with where to
find each credential. The services, grouped:
| Service | Credentials needed | What lights up |
|---|---|---|
| Google Cloud | GOOGLE_CLOUD_PROJECT, ADC or service account JSON |
Gemini reasoning for all agents, Firestore persistence |
| Grafana — Prometheus | GRAFANA_PROM_URL, GRAFANA_PROM_USER, GRAFANA_PROM_API_KEY (metrics:write) |
Agent event metrics, crew risk gauges, LLM latency |
| Grafana — Loki | GRAFANA_LOKI_URL, GRAFANA_LOKI_USER, GRAFANA_CLOUD_API_KEY |
Structured agent logs, closeout reports |
| Grafana — Agent O11y | AGENTO11Y_ENDPOINT, AGENTO11Y_AUTH_TENANT_ID, AGENTO11Y_AUTH_TOKEN |
Per-generation LLM tracing (token usage, latency) |
| Grafana — MCP | GRAFANA_MCP_API_KEY (or GRAFANA_CLOUD_API_KEY), GRAFANA_URL |
Closeout reports via MCP server (Loki + Prometheus + dashboards) |
| Firebase | FIREBASE_PROJECT_ID, FIREBASE_API_KEY |
Auth (Google sign-in), Firestore data store |
| AviationStack | FLIGHT_STATUS_API_KEY |
Live flight status for TRAFFIC agent |
| Twilio | TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE_NUMBER |
SMS outreach for WRANGLER agent |
Each integration degrades gracefully — if a key is missing, that service
disables with a log line and the rest of the system continues. Fixture mode
(pnpm demo / pnpm tape) needs none of them.
See docs/runtime.md for IAM roles and deployment details.
Fixture mode is the supported demo path. Live mode requires credentials we cannot distribute; see What's here, and what isn't.
Not one model with a large prompt. Eight specialists, each owning one domain,
holding one credential, reading only what its job requires. Crew is provisioned
per show — a domestic regular-season game doesn't call CUSTOMS to work.
| Agent | Owns | Live integrations |
|---|---|---|
SPOTTER |
The show: game state, clock, weather delay, rundown overrun → projected final | ESPN scores, Weather.gov, Gemini end-time prediction |
TRAFFIC |
The network: flight status, ground stops, airport ops, last-shuttle timing | AviationStack API, curated 28-airport route network |
ADVANCE |
The roster and the next call — and how we know (crew sheet, crew-confirmed, or inferred) | Firestore crew/assignment store |
WRANGLER |
Brokering constraints with crew without requiring them to disclose the commitment | Twilio SMS, Gemini NL constraint parsing |
STEWARD |
The rule set: turnaround, rest, meal penalty, travel-time treatment | Gemini agreement interpretation |
FIXER |
Alternatives: inventory, fare rules, policy compliance, seat holds | Gemini option generation and ranking |
RUNNER |
Carrying the decided plan to a human desk for execution | TMC handoff protocol |
CUSTOMS |
Carnets, work permits, border clearance — off for domestic shows | — |
Each agent emits structured events to the message bus. Every event records the agent, the type, the show, a timestamp, and a provenance envelope — forming a complete epistemic trail that Grafana Loki indexes and the closeout report queries.
The crew is split for a security reason, not an org-chart reason. An agent that reads live external feeds is an agent that can be fed something hostile. An agent that can book travel is an agent that can spend money. Apron never makes them the same agent.
-
The agents that read the outside world cannot act on it.
SPOTTERandTRAFFICingest untrusted third-party data — scoreboard feeds, weather services, carrier status APIs. Zero write authority, zero access to personal data. Content arriving in those feeds is treated as an observation to be evaluated, never as an instruction to be followed. -
The agent with booking authority never sees a name.
FIXERsearches inventory and places holds against tokenized travelers — a loyalty tier, a home market, a rest window, a gear profile. Re-identification happens desk-side, behind the human gate. -
Nothing irreversible happens without a person. Agents hold; people ticket. Every action that spends money, cancels inventory, or reaches a crew member passes a named human with the full option set in front of them. The autonomy is in the analysis, not the authority.
Enforced by the scoped credential broker in
packages/credentials/, not by prompt instruction.
| Agent | Reads | Authority | Never receives |
|---|---|---|---|
SPOTTER |
Game feeds, clock, weather, rundown | read-only | Crew identity · PNRs · personal data |
TRAFFIC |
Carrier status, ground ops | read-only | Crew identity · next calls · fare data |
ADVANCE |
Crew sheets, call sheets, provenance | roster state | Payment instruments · fare detail · HR records |
STEWARD |
Rule packs, wrap/call timestamps | rule flags | Contact info · payment · personnel files |
WRANGLER |
Constraints crew choose to disclose | constraints | Undisclosed next-call detail · third-party call sheets |
FIXER |
Inventory, fare rules, policy, tokenized traveler | hold only | Names · contact details · payment instruments |
RUNNER |
The approved decision record | handoff | No outbound channel to crew, ever |
CUSTOMS |
Carnet requirements, permits, border rules | doc flags | Fare data · payment · financial records |
Full write-up: docs/agent-separation.md.
Every fact on the board records which agent produced it, which source it came from, how confident it is, and who is allowed to see it. Two consequences:
- Inferred ≠ confirmed. Apron can infer a likely next call from return routing. It marks that inference in plain sight and refuses to rebook against it until a human or the crew member confirms. Guesses don't spend money.
- Whoever owns the fact decides who sees it. If a crew member's next call is
on another network's show, the production seat sees
External call · withheld— never the network, the show, or the client. The system still solves for it, becauseWRANGLERcan broker the constraint ("must be at MCI by Mon 13:00 CT") without the commitment behind it.
Envelope schema: docs/provenance.md.
No crew location tracking. No message or SMS interception. No rate or day-rate data. No performance or reliability scoring of individual crew. This protects call times; it is not a surveillance layer on freelancers.
All three are imported and called at runtime, not just named here.
| Where | What it does | |
|---|---|---|
| Google Cloud — Gemini 2.5 Flash | integrations/google-cloud/ |
Reasoning for every agent: end-time prediction in SPOTTER, agreement interpretation in STEWARD, natural-language constraint parsing in WRANGLER, option generation and ranking in FIXER. Stubs responses in fixture mode so the demo runs without credentials. |
| Grafana — MCP + Telemetry | integrations/grafana/ |
Grafana Cloud MCP server connection (@modelcontextprotocol/sdk) for closeout report generation — query_loki_logs (LogQL), query_prometheus (PromQL), and search_dashboards tools are called at runtime to query the agent decision trail and operational metrics. Three telemetry write pipelines run in parallel: Prometheus metrics, Loki structured logs, and Agent Observability SDK. See Grafana MCP integration for details. |
Entry points:
packages/server/src/server.ts— where agents are registered and integrations are boundpackages/orchestrator/src/runtime.ts— agent runtime, credential broker, dispatch with latency trackingintegrations/grafana/src/mcp-client.ts— Grafana Cloud MCP client (Streamable HTTP,@modelcontextprotocol/sdk)integrations/grafana/src/closeout.ts— closeout report builder (MCP primary, direct HTTP fallback)integrations/grafana/src/reporter.ts— Prometheus remote write (Influx line protocol)integrations/grafana/src/loki.ts— structured log push to Lokiintegrations/grafana/src/agento11y.ts—@grafana/agento11ySDK initialization
Apron treats observability as a first-class product concern, not a debug afterthought. Three pipelines run in parallel:
Every agent event pushes an apron_agent_event sample via Influx line protocol
to Grafana Cloud's remote-write endpoint. Additional metrics track crew risk in
real time:
| Metric | Type | What it measures |
|---|---|---|
apron_agent_event |
gauge | One sample per agent action, tagged by agent + event type |
apron_crew_at_risk |
gauge | Current count of crew members at risk per show |
apron_call_times_exposed |
gauge | Crew whose next call is breached |
apron_show_state_change |
gauge | State transitions: clear → watch → risk → down |
apron_agent_process_duration_ms |
gauge | LLM processing latency per agent dispatch |
Every agent event is logged with structured labels (agent, event_type,
game_id, show_id), JSON-parsed at query time. This forms the epistemic
history — not a debug log but a queryable record of how operational facts
evolved: when the projected final shifted, when a constraint was confirmed, when
a provenance level was upgraded.
The closeout report (GET /api/closeout/:gameId) queries this trail to build a
chronological narrative: timeline, decisions, compliance summary, flight
disruptions, and an operational grade.
The @grafana/agento11y SDK (v0.13.0) instruments every Gemini generation with:
normalized prompt and completion text, token usage (input/output/total), latency,
model version, and per-agent tagging. Telemetry flows to the Grafana Cloud
Agent Observability endpoint.
An importable dashboard ships at
integrations/grafana/dashboards/apron-ops-overview.json:
show status row (crew-at-risk, call-times-exposed, state changes), agent
activity (events/min time series, events-by-type bar gauge), Gemini LLM
performance (latency time series, P95 per agent), and a Loki log panel.
The Grafana Cloud MCP server is the primary data interface for closeout
report generation. After a game ends, the closeout builder connects via
Streamable HTTP (@modelcontextprotocol/sdk) and calls three MCP tools:
| MCP tool | Query | What it provides |
|---|---|---|
query_loki_logs |
{app="apron",game_id="..."} |
The full agent decision trail — every event from SPOTTER, TRAFFIC, STEWARD, ADVANCE, WRANGLER, FIXER, and RUNNER |
query_prometheus |
max_over_time(apron_crew_at_risk[24h]), count_over_time(apron_show_state_change[24h]), avg_over_time(apron_agent_process_duration_ms[24h]), count_over_time(apron_agent_event[24h]) |
Operational metrics snapshot: peak crew at risk, state transitions, agent latency, total events |
search_dashboards |
apron |
Link to the Grafana ops dashboard, included in the report |
The closeout report synthesizes these into a structured operational
narrative: timeline, key decisions, compliance summary, flight disruptions,
and an operational grade (A–F). The /closeout admin view renders the
report with the MCP data source tagged on each section.
Fallback: When the MCP server is unavailable (missing credentials or
connection error), the builder falls back to direct Loki HTTP queries. The
report's source field ("mcp" or "direct") records which path was
used.
GRAFANA_MCP_API_KEY=glc_... # Grafana Cloud API key (or reuses GRAFANA_CLOUD_API_KEY)
GRAFANA_URL=https://<stack>.grafana.net
GRAFANA_MCP_URL=https://mcp.grafana.com/mcp # default; override for self-hosted
For unattended deployments (Cloud Run), use the open-source Grafana MCP
server with a service-account
token. Set GRAFANA_MCP_URL to the local server's endpoint.
integrations/grafana/src/mcp-client.ts—GrafanaMcpClientclass: connects viaStreamableHTTPClientTransport, wrapsquery_loki_logs,query_prometheus, andsearch_dashboardswith typed return values and error tracking.integrations/grafana/src/closeout.ts—CloseoutBuilder.generate(): MCP primary path queries Loki via MCP for the agent event trail, then Prometheus via MCP for operational metrics, then searches dashboards for a direct link. Falls back to direct Loki HTTP.packages/server/src/server.ts— initializesGrafanaMcpClientat startup, attaches to the closeout builder. Health exposed atGET /api/integrations/health(.grafana.mcp).packages/board/closeout.html— admin view at/closeoutlisting completed games with report generation.
This repository is complete and runnable. Nothing is stubbed to hide it. What is not here is not here for a reason we can state:
| Not included | Why |
|---|---|
| Production rule packs (encoded provisions for specific agreements and locals) | Collective bargaining agreements are copyrighted works of their parties. We don't republish them. A clearly-marked sample pack ships in packages/rules/packs/sample-local/ and runs the demo. |
| Real TMC adapters | Built against specific mid-office/GDS environments under NDA. A mock adapter in integrations/tmc/mock/ runs the full scenario. |
| Credentials and service configuration | Obvious. .env.example documents every variable. |
| Any real crew, itinerary, or PNR data | Personal data. Fixtures are synthetic. |
The rule engine is here. The orchestrator is here. The credential broker is here. All eight agents are here. The board is here.
packages/
types/ shared TypeScript types and interfaces
orchestrator/ agent runtime, message bus, scenario playback
credentials/ scoped credential broker ← enforces the capability matrix
provenance/ envelope schema + validators
agents/
spotter/ traffic/ customs/ external-world readers, read-only
advance/ wrangler/ steward/ roster, constraints, rule evaluation
fixer/ runner/ options/holds, human handoff
rules/
engine/ the interpreter
packs/sample-local/ illustrative only — see NOTICE
server/ HTTP + WebSocket server (no Express — native node:http)
board/ watch board, admin console, agent dashboards
index.html TMC desk + production seat
admin.html account/game/crew/assignment management
traffic.html TRAFFIC flight status monitor
wrangler.html WRANGLER constraint gathering
spotter.html SPOTTER game prediction
steward.html STEWARD compliance view
closeout.html closeout reports admin view (Grafana MCP)
featured.html public featured-game board
fixtures/
alcs-gm4/ twelve-inning night (16 steps, 14 crew, 6 shows)
tape/ demo tape variant (16 steps, 22 crew, full seat-switch)
integrations/
google-cloud/ Gemini via Vertex AI (stubs in fixture mode)
grafana/ Prometheus + Loki + Agent O11y + MCP client + closeout
src/mcp-client.ts Grafana Cloud MCP server client
src/closeout.ts post-game closeout report builder
dashboards/ importable Grafana dashboard JSON
firebase/ Firestore persistence (accounts, crew, games, assignments)
aviationstack/ AviationStack flight status API (TRAFFIC agent)
twilio/ Twilio SMS (WRANGLER crew outreach)
tmc/mock/ runs the demo without a live TMC
site/ marketing site (static HTML)
docs/
architecture.md system architecture + diagram
agent-separation.md security model deep-dive
provenance.md envelope schema + disclosure rules
runtime.md env vars, IAM roles, deployment
Contributions are welcome. Because Apron is dual-licensed, all contributors must sign the CLA before a pull request can be merged — see CONTRIBUTING.md. Without it we lose the ability to offer the commercial terms in COMMERCIAL.md.
GNU AGPL-3.0. If you run a modified version as a network service, you must make your modified source available to that service's users.
A commercial license is available if those terms don't fit — see COMMERCIAL.md.
Trademarks are reserved and are not granted by the AGPL. Forks must be renamed; see NOTICE.
The sample rule pack is illustrative and must not be relied on to determine any real obligation under any collective bargaining agreement or employment contract.