Skip to content

DX: developers cannot obtain a credential matching a published app's permissions #494

Description

@jamesbhobbs

TL;DR

A developer can't get a credential that matches production, so local success doesn't predict published success.

The two credentials

  • A personal API token — the only kind an individual can mint. Full access to all of /v2.

  • A static app viewer token — what a published app actually runs on. Minted only server-side, handed to the app for a signed-in viewer, with a short (~15 minute) life. It allows exactly three operations:

    • GET /v2/notebooks/:id — block content stripped, last-run pointers nulled
    • POST /v2/runs — detached runs only
    • GET /v2/runs/:runId — the viewer's own run only, raw snapshot withheld

    Everything else → 403 This endpoint is not available to static app tokens.

Why that's a DX problem

  1. No way to test the real surface. You cannot obtain a restricted token, so every local run necessarily has more authority than production. Publishing is the only functional test.
  2. The gap is silent. Off-allowlist calls 403, and well-written clients swallow that into an empty state — so a feature disappears rather than errors. Nothing points at permissions as the cause.
  3. Same URL, different payload. Even the allowed routes return less data in production (stripped content, nulled run pointers, no raw snapshot). A client can depend on a field that is always null once published — a 200 either way, so no error surfaces at all.
  4. Devs hardcode the policy. The workaround is guessing in client code — if (isEmbedded) return in examples/local-runner/cloud-app/index.html, where isEmbedded is merely window !== window.parent. That's an assumption about server behavior baked into a client, free to drift silently.

The concrete instance

The cloud-app run-history panel worked all through development (older ?token= path, personal token) and vanished when published, because enumerating a notebook's runs is off the allowlist. No error, just a missing panel.

Two candidate fixes

  • Emulate the restrictions in the CLI — duplicates the policy in a second place, where it drifts, and would almost certainly miss the field-level redactions.
  • Issue a real restricted token to developers (the preferred direction) — local requests then traverse the actual production guard, so parity holds by construction, including the redactions. The server already performs the equivalent authorization check when it mints one of these tokens today, and such a token is less privileged than what the developer already holds, so it isn't an escalation. Main open question is lifetime, since ~15 minutes is painful for a dev loop.

Also worth noting

There's no capability discovery — even with a restricted token, a client learns its limits only by collecting 403s.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions