Embedded views
iframe a live, read-only, branded view of one run — no credential in the browserWhat an embed is
An embed grant lets an external service (typically an LMS) drop a live view of one run — logs, metrics, streams, artifacts — inside its own page via an <iframe>, without ever handing a Симолёт credential to the browser. The embed token is the only thing the browser holds, and it is:
- read-only — admits only a narrow whitelist of GET sub-resources of its one run;
- single-job — bound to exactly one job id, checked in the auth guard and re-checked by the tenancy gate;
- expiring — a signed expiry (default 24h, capped at 30 days); fail-closed once past;
- allow-scoped — the grant may further narrow which categories (
logs/metrics/streams/artifacts) are reachable; an empty set means all; - branded — an optional title / logo / accent for the stripped chrome (non-secret, content-addressed).
It’s a richer sibling of the Google-Docs-style share link (/s/{code}): where a share link is a durable URL you paste into chat, an embed is a live, service-driven, session-scoped view.
Mint a grant (service-only)
POST /v1/jobs/{id}/embed mints the grant. Minting is service-only: the caller must already be able to see the run and hold a runs management scope (runs:manage / runs:share / runs:submit, or *). A read-only share/embed view cannot mint further grants, and a viewer cannot mint. Requires the auth store (else 503).
curl -X POST $BASE/v1/jobs/<job_id>/embed \
-H "Authorization: Bearer slt_…" -H "content-type: application/json" \
-d '{
"ttl_hours": 12,
"allow": ["logs", "metrics"],
"branding": { "title": "Lab 3 — Hover Task", "accent": "#5b8cff" }
}'
# → {
# "token": "…",
# "url": "https://…/embed/e/<token>", # ← iframe this
# "expires_at": "2026-07-14T12:00:00Z"
# }The body is optional — POST with no body mints a full-feature, unbranded grant at the default TTL. Then set the returned url as the iframe src; it 302-redirects to the stripped, branded SPA view carrying only the token and its one job id.
One-shot: mint at submit time
The LMS flow that launches a run and shows it immediately: add ?embed=1 to a run or job submit and the response carries the created run plus an embed grant — the branded live view is ready without a second round-trip.
curl -X POST "$BASE/v1/runs?embed=1" \
-H "Authorization: Bearer slt_…" -H "content-type: application/json" \
-d '{ "stack": "mujoco", "command": ["python", "main.py"] }'
# → { …job fields…, "embed": { "token": "…", "url": "https://…/embed/e/<token>", "expires_at": … } }What the embed page reads
The stripped SPA view resolves the token to its grant with GET /v1/embed/grant — self-authenticating via the token (as the X-Embed-Token header or the ?embed= query param), no principal needed. It returns the job, the allow-set, the expiry, and the branding.
curl "$BASE/v1/embed/grant" -H "X-Embed-Token: <token>"
# → { "job": "job_…", "allow": ["logs","metrics"], "exp": 1760000000,
# "branding": { "title": "Lab 3 — Hover Task", "accent": "#5b8cff" } }From there the view reads only the whitelisted sub-resources of that one job (the run view, logs/events, recording, streams, artifacts) — each further gated by the grant’s allow-set. Any other path, any other job id, or any write fails closed. A missing / invalid / expired token is a 401.
POLYGON_EMBED_SECRET (or the shared share secret) to invalidate every outstanding grant at once.