Skip to content

App-shelf: extension service serves the app bundle + proxies the engine (fork-cutover static and proxy slice) #822

Description

@aarontrowbridge

Important

Problem — The fork-cutover is half-landed: the app overlay source now composes onto stock canonical v1.18.29, but the extension service cannot SERVE the app — stock's engine serves only its embedded web UI, so the framed amicode app must come from the extension service, which today serves only the /amicode/* route table. Until it can serve the app dist and front the engine's API, the panel keeps iframing the fork binary's origin and the vendored pin cannot flip to stock (the fork cannot retire).
Approach — Give the extension-host amicode service an app shelf: static serving of the built app-bundle dist (SPA fallback, content types, traversal refusal) plus a reverse proxy to the spawned engine (method/body/SSE passthrough), with precedence exact /amicode/* routes → static hit → engine proxy. Auth: the service accepts its own per-boot mint AND the engine token on its routes, and the proxy forwards the engine token unchanged — so the framed app bootstrapped with the engine credential works everywhere with zero app-side change.
Scope — in: the shelf + proxy modules in the service; the service learning the engine upstream (URL + mint) at boot; packaging the built app dist into the extension (CI build per the app-bundle README's proven recipe: materialize → bun install → vite build); a dev override dir setting with honest needs-setup when no dist exists; the env-gated live boot proof (telaio_app_probe precedent). · out: the origin flip itself (separate issue, blocked by this one); any overlay/app source change.

Acceptance Criteria

  • GET / on the service origin serves the app dist's index document; asset requests get correct content types; unmatched GETs fall back to the SPA index; path traversal is refused
  • Non-amicode, non-static requests proxy to the engine upstream with method, headers, and body preserved; SSE (/event) streams unbuffered through the proxy
  • The route precedence is asserted: an exact /amicode/* route never shadows behind the proxy, and a static asset hit never reaches the proxy
  • Auth: a request bearing the engine token is accepted on /amicode/* routes (alongside the service's own mint); proxied engine requests carry the engine token unchanged
  • With no dist present and no override set, the shelf serves an honest needs-setup placeholder (never a silent 404-as-app)
  • The boot proof (env-gated, CI-skipped) boots the real service against the real spawned engine + built dist and asserts: app document from the service origin, an engine API call through the proxy, an SSE connect through the proxy, and one /amicode/* route — end-to-end from the service origin

Testing Decisions

Reuse the service's existing vitest in-process harness (vscode-free, per-boot mock auth) for all shelf/proxy/precedence/auth tests; a local mock engine upstream (node:http) for the proxy tests. The live boot proof is env-gated like the telaio probe — the gate runs it for real once, CI skips it.

Key Decisions

  • The dist is a BUILD PRODUCT packaged with the extension (the fetch:opencode precedent for riding the VSIX); the override setting is the dev escape hatch, not the primary path.
  • The proxy is deliberately transparent (no header rewriting): the app's own auth machinery is the same one that works against the engine today; accept-engine-token on /amicode/* is the one service-side addition.
  • SPA fallback serves index.html only for GETs that accept HTML — the API surface keeps honest 404s.

Constraints & Invariants

  • The service stays vscode-free (unit-testable), stateless across requests, and never-throwing per its founding discipline; a shelf failure must degrade honestly (needs-setup/placeholder), never kill the /amicode/* route table.
  • No overlay/app source changes in this slice; no change to the vendored binary pin; the panel keeps iframing the engine origin until the origin-flip slice.
  • The parallel-run posture holds: the shelf is additive until the cutover.

Prior Art

The service's founding slice (#451 M1, its route table + auth idiom); the telaio file-server SPA-fallback + content-type + traversal-refusal pattern (telaio loops 19–20); the telaio boot-proof/port-audit probe (#682); the app-bundle README's build recipe; the phase-2 disposition (recon artifact) — the shelf was named the cutover's missing piece.

Source

Fork-retirement blockers from the harness-agnostic campaign's loop 28d (amicode main @ 4ef3191 landed the overlay re-base). Part of the fleet-amicissimo-forksplit synthesis campaign. The origin flip is the follow-up slice (blocked by this one); the vendored pin flip to stock rides that slice.

Notes

Label hitl — human merge gate. Worktree off origin/main; the shared checkout's branch is never touched.

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

    hitlNeeds human decision or review

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions