Skip to content

Serve a Mini App page through its own proxy #255

Closed

Description

@Fectivnfy112357

Parent

#251

What to build

The service can serve a Mini App's page through a per-Mini-App loopback proxy, with the credential and
the page bridge injected, without yet enabling the capability for this client's runtime owner.

Scope note

Criterion "with explicit Mini App options and no declared owner kind, the service starts a Mini App and
produces a view URL for it" moved to #256. Producing that URL needs the client-facing operation, and the
only legal route to the Mini App's origin and credential is a runtime-port capability member — which
packages/webui/AGENTS.md says fails pnpm typecheck:webui without a binding. Its consumer is the open
operation in #256, and ADR 0020 already schedules the proxy's consumer with the cell, the presenter and
the view as one change.

What is delivered here: the proxy itself, its per-plugin stable port, credential injection, unchanged
paths, forwarded upgrades, the framable document and the bridge seam.

Acceptance criteria

  • Fetching that URL returns the page the Mini App's own process serves, with the bridge injected and
    the document framable.
  • Paths are forwarded unchanged, including the absolute paths a page uses for its own subresources.
  • The credential is added on the outbound request and never appears in a response, in the view URL,
    or in anything sent to the client.
  • WebSocket upgrades to the Mini App are forwarded under the same rules as requests.
  • One plugin's view URL is stable across a restart of the service, so the page's own storage survives.
  • Nothing in this change flips the Mini App service on for this client's runtime owner.

Activity

  1. added
    ready-for-agentFully specified, ready for an AFK agent
    miniappWebUI Mini App hosting programme (ADR 0020)
    on Oct 26, 2026
  2. Fectivnfy112357 commented on Oct 26, 2026

    @Fectivnfy112357
    Author

    Six of the original seven criteria are proven; the seventh moved to #256 because its consumer lives there.

    Branch feat/miniapp-proxy, commits e2ca0be3 → 55b90b08 → 1f009695, stacked on #263. Suite:
    packages/webui/test/unit/miniapp-proxy.test.ts, 9 cases against a real upstream http.Server.

    1. Fetching the view URL returns the page the Mini App's process serves, bridge injected, document framable

    startMiniAppProxy returns viewUrl; fetching it returns the upstream body. The document is the one
    response the proxy rewrites: a script tag for the bridge is injected before </head>
    (injects the bridge before the document head ends), after the opening <body> tag when a page has no
    head (injects before the body when the document has no head), and ahead of the document when it has
    neither. The bridge script is served by the proxy itself at its own path and never forwarded upstream
    (serves the bridge itself and never forwards that path upstream).

    Framability (makes the document framable: drops X-Frame-Options and the frame-ancestors directive):
    X-Frame-Options is dropped, frame-ancestors is removed from the page's CSP while default-src and
    script-src survive. Nothing is added. The rules are now recorded in ADR 0020 under
    "The proxy makes the document framable", which settles that record's first open question; the bridge's
    method-level error mapping is the only one left.

    The bridge body arrives with #260 — this ticket owns which document, where the tag goes and what
    headers survive.

    2. Paths are forwarded unchanged, including the absolute paths a page uses

    forwards the path unchanged and returns the upstream response: /echo reaches the upstream as /echo.
    The proxy is one listener per Mini App rather than one origin with a path prefix precisely so that
    /dashboard, /api/... and asset URLs need no rewriting.

    3. The credential is injected outbound and never reaches the client

    injects the credential outbound and never returns it to the caller: the upstream observes the
    credential header, and the response the caller receives has no such header. viewUrl is
    http://127.0.0.1:<port> — the credential is not in it and is not otherwise exposed.

    4. WebSocket upgrades are forwarded under the same rules

    forwards an upgrade with the credential: the caller receives the upstream's 101, and the upstream
    observes the credential on the upgrade request. A closed peer tears down its pair; without that, a
    half-closed pipe held the upstream server open forever, which is how the test teardown first failed.

    5. One plugin's view URL is stable across a restart

    keeps one plugin on the same port across a restart, so the page storage survives: start on the derived
    port, close, start again from a fresh derivation, identical viewUrl. The port is a pure function of the
    plugin id because the page's localStorage and IndexedDB are keyed by its origin.

    6. Nothing here flips the service on for this client's runtime owner

    Diff scope confirms it: this branch touches packages/webui/src/server/miniapp/ and its test only. No
    owner-policy cell, no capability flag, no service composition.

    Moved to #256: starting a Mini App with explicit options and producing its view URL

    The ticket body now says so. Producing that URL needs the client-facing operation, and the only legal
    route to a Mini App's origin and credential is a runtime-port capability member —
    packages/webui/AGENTS.md states that such a member fails pnpm typecheck:webui without a binding, and
    its consumer is #256's open operation. ADR 0020 already schedules the proxy's consumer with the cell, the
    presenter and the view as one change.

    Gates at 1f009695

    $ pnpm typecheck:webui    # server, client and test projects, passed
    $ pnpm check:source       # 5459 files, passed
    

    pnpm check:webui-dependency fails on a violation this branch did not introduce: document.fonts.ready
    used from client/features/workspace/ui/WorkspacePanels.tsx:489,534, a bindings-layer file that already
    fails at the base commit 9713c4cf. The new module contributes zero violations
    (grep -c "miniapp/proxy" over the report). That failure will follow every branch in this stack until
    the panel file is fixed.

    Not run: pnpm verify in full; browser, package and release steps are left to CI.

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

    miniappWebUI Mini App hosting programme (ADR 0020)ready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions