Skip to content

Connections panel — credentialed backends (Company Compute + Pasqal Cloud) in the app UI #159

Description

@kateebonner

Important

Problem

There is no unified in-app surface for connecting to external backends: one keyboard-only command for one backend (the just-pushed key-entry flow), nothing for Pasqal beyond hand-set env vars riding the agent's transcript, and no visible connection state — the 2026-07-19 submitter-drift incident burned 30 minutes because nothing showed who the backend thought the caller was.

Approach

A Connections panel in the app UI (a tab beside the existing status/vaults surface) backed by amicode-owned routes in the vendored app server, which owns the credential store and both validators and answers key entry synchronously in one round trip (ADR 0002). Company Compute keeps the pushed hp-cloud-key contract (pending merge): validate → save → flip solver mode to HP. Validation is probe-first — the authorizer-probe trick from hp-cloud-key works against the live service today; it upgrades to identity echo ("Connected as <submitter>") when the identity endpoint ships (companion aws-infra issue).

Approaches Considered

  • Server-route seam (chosen) — store + validators in the fork server; won the 3-lens design panel on UX (5/5) and maintainability (4/5); security closed by grafts.
  • File seam (runner-up) — extension owns store, request files + poll-watcher; best raw security but structural 1–2s feedback latency and no standalone-serve path.
  • Loopback bridge — extension HTTP listener; rejected: new secret + unprecedented listener, worst CLI parity.
  • New VSCode webview panel — rejected in design review: splits the surface away from the solver toggle users already know.

Scope

  • In: panel UI (Company Compute + Pasqal cards); server routes (status / submit / disconnect / revalidate); credential store behind one storage seam (ADR 0001); both validators; HP flip on valid Company Compute key; staleness + drift rendering; the amico-pasqal launcher (secrets off the transcript); the hardening grafts (ADR 0002); migrating the in-flight key-entry command's writer onto the storage seam once its branch merges.
  • Out: per-solve routing UX — Δ10-UX — per-solve routing UX (local vs company-compute) #63 owns it. Estimator — Δ10 — Estimator integration (v0 = tshirt_sizing) + per-solve routing UX #34. Device selection/submission — the Pasqal device-path issue. Identity endpoint server-side — the aws-infra companion issue. Keychain storage — deferred per ADR 0001's seam.

Assumptions / Open Qs

  • hp-cloud-key merges roughly as pushed; its command remains a working secondary entry sharing contracts with the panel via golden fixtures.
  • The Pasqal SDK auth call used by the connectivity spike remains the validation vehicle (auth only, no job), extended to mint + return the token for persistence; token refresh against the SDK's expiring-token-provider is assumed workable, with session-only as the recorded fallback.
  • Python resolution on GUI-launched macOS needs the documented escape-hatch setting; misconfigured-python must render distinctly from unreachable-service.

Acceptance Criteria

  1. Entering a valid Company Compute key in the panel writes the cloud credential file (0600 at birth, byte-shape readable by the real CLI reader), shows Connected, and flips solver mode to HP through the existing switch machinery — one user action.
  2. Entering an invalid key shows invalid-credentials; nothing is written, nothing flips. An empty submission is a no-op (nothing written, no state change). An unreachable service renders as its own state with distinct copy.
  3. Entering valid Pasqal credentials performs an auth-only validation (no job submitted), exchanges them for a token, persists only the token (0600 at birth), and shows the project id + available devices; a test asserts the password appears in no file under the credential/ops directories after the flow completes.
  4. Secrets never appear in URLs, argv, logs, chat transcripts, or any status/error payload — an adversarial redaction test seeds secrets and asserts they cannot survive into any rendered surface.
  5. A hand-edited credential file renders a stale badge and triggers background revalidation; an unparseable credential file renders needs-key (never a crash or a stale badge); an offline boot renders "last verified as ", never a false invalid.
  6. Against a stubbed identity endpoint, the panel shows "Connected as <submitter>", and a submitter change renders as an explicit diff against the stored identity (the drift canary), never a silent overwrite. (The live-endpoint version belongs to the aws-infra companion issue.)
  7. Key entry works under standalone opencode serve with no extension host running.
  8. Mutation routes refuse to serve when the server is bound beyond loopback; route auth is enforced by a per-boot password (no unauthenticated localhost access from the extension-spawned path).

Key Decisions

  • Seam: server-route (ADR 0002). Storage: disk, 0600, one storage-seam interface (ADR 0001). Validation: real handshake, probe-first with identity upgrade.
  • HP flip on valid Company Compute key is the pushed hp-cloud-key contract (pending merge) — kept as-is (grant entitlement + request switch; the running watcher does the re-prep exactly once).
  • HP flip works headless-safely: the fork route grants the entitlement and writes the durable switching request through the shared ops-dir contract; under an attached extension host the watcher does the full re-prep immediately, and under standalone serve the credential still validates and saves, with the re-prep completing on next extension attach.
  • Pasqal persistence is token-only (ADR 0001): username/password live in memory only at entry — the validation handshake mints an Auth0 token via the SDK's token-provider mechanism, and only the token persists; runtime auth uses the stored token. If refresh proves unsupported, fall back to session-only (re-prompt), never password-on-disk.
  • Data contracts: Company Compute file {base_url, token} (frozen — the CLI reader is untouched). Pasqal file {project_id, token} (+ expiry metadata; exact shape finalized against the SDK — the invariant is no password at rest). Status payload (secret-free): {id, state: connected|needs-key|invalid|expired|unreachable|unentitled|validating, identity?, entitlements?, expires_at?, devices?, validated_at, stale}identity is the panel-side field, equal to the identity endpoint's submitter for Company Compute and the project id for Pasqal; entitlements and the unentitled state carry the service-side sense of the glossary's Entitlement entry and are forward-compatible only — populated once the service-side classification follow-up ships (until then the fail-closed authorizer 401s such keys, which render as invalid); expired renders as a reconnect prompt driven by expires_at; devices is the Pasqal device list, persisted as non-secret connection metadata and refreshed on every (re)validation — it is the contract the device-path issue reads for device selection. Probe classification: 401 → invalid; 403/404/2xx → valid; else → error (no save).
  • Validator child processes get a minimal env (credentials + interpreter path only), never a full env spread.

Constraints & Invariants

  • The Company Compute credential file's shape and location are frozen; every existing CLI/agent consumer must work unchanged.
  • Submitter is an immutable join key: render drift, never reconcile it silently.
  • Secrets ride only: POST bodies (route-auth'd), the Authorization header, child-process env, and 0600 files. Nowhere else.
  • The Pasqal password is never at rest — in-memory only during entry/validation/token-mint.

User Stories

  • A researcher pastes the company key once and their next big solve can go to company compute; the panel shows who they are to the service.
  • A hackathon user connects Pasqal credentials and sees which devices they can reach, without ever typing a password into a chat.
  • A CLI user hand-edits their credential file; the panel notices, revalidates in the background, and reflects reality.
  • During an auth incident, anyone can open the panel and read the identity the service resolves for their key — drift is visible in one glance.

Modules & Interfaces

  • Fork server, connections module — routes: status (GET, secret-free), submit-credential (POST body), disconnect, revalidate; owns the store + validators; registered beside the existing amicode route block.
  • Fork UI, connections tab — presentation-only cards per connection; tolerant wire parser as its own module, per the existing vaults-tab idiom.
  • Storage seam — one interface, per-connection-type file backends; keychain is a future second implementation.
  • Pasqal auth validator — auth-only sibling of the connectivity spike's script; env-only inputs; validates, mints the token, emits one JSON line (token + project + devices); exit codes map to the three failure classes.
  • amico-pasqal launcher (CLI package) — reads the Pasqal token file, env-injects the token, execs the connector (which authenticates via the SDK's token provider); removes secrets from the agent's command lines.
  • HP flip — reuses the existing entitlement-grant + solver-mode-switch machinery; no new switch path.

Testing Decisions

  • Temp-dir env-override harness for all file paths; handshake stubs for both services (no network in unit tests); a fake interpreter shim asserting env-carriage and argv-cleanliness; golden fixtures for the two cross-repo contracts (credential file byte-shape parsed by the real CLI reader; validator exit-code/stdout contract); mode-at-birth assertions on the tmp file; the redaction poison test; existing boot-smoke stays scope-pure.

Risks

  • Validation logic lives in the vendored binary — urgent fixes are rebuild + re-vendor. Accepted in ADR 0002; revisit per its flip condition.
  • Fork route-registration is the highest-conflict file on upstream rebases.
  • Multiple uncoordinated credential-file writers (panel, setCloudKey command, hand edits) — last-writer-wins, detected by staleness, not prevented.

Source

Notes

Design produced via a 3-candidate / 3-judge / synthesis panel (2026-07-19); the runner-up and flip condition are recorded in ADR 0002.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:uienhancementNew feature or requesttrack:cloudTrack C — company compute / cloud solve-service

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions