Skip to content

Fleet Studio #1524: base peer-observation routes mount without hub-activation - #1535

Merged
jeonghun-jj-lee merged 1 commit into
feature/free-tier-fleetfrom
opencode/fleet-studio-1524-observe-without-hub-activation
Sep 24, 2026
Merged

jeonghun-jj-lee merged 1 commit into
feature/free-tier-fleetfrom
opencode/fleet-studio-1524-observe-without-hub-activation

Conversation

@jeonghun-jj-lee

Copy link
Copy Markdown
Contributor

Closes #1524. Base: feature/free-tier-fleet.

What & why

JJ reported his Mac Studio's session never appears in the MacBook UI. Root cause (one of two — the server half): GET /amicode/fleet/sessions 404s on a machine that holds a perfectly valid fleet-peer roster + reader tokens, because the fleet routes mount only inside the hub-activation gate. resolveFleetActivation arms only when amicode.fleetHubUrl and amicode.fleetTunnelAlias are set; JJ's machine has neither, so opts.fleet was never passed and registerFleetRoutes never ran. The base peer-observation path (baseStudioActivates, #1478 — designed for exactly this unentitled serving-peer case) was nested inside that gate, so it could never fire without hub config.

Verified live (authenticated with the handshake credential): /amicode/fleet/sessions → 404, /amicode/fleet/status → 404, /amicode/fleet/attachment → 200 (unconditional), /amicode/roster → 200 listing both machines serving∧reachable.

Change

  • observationOnly fleet signal (index.ts): when set, createAmicodeService bypasses stageFleetDataPlane/the premium plane entirely (AC5 — no premium plane stages even with a resolvable entitlement) and consults only baseStudioActivates to mount the read/observation routes. Undefined/false → today's exact path (byte-identical for entitled/armed machines).
  • Wiring (amicode_service_wiring.ts): when activation is unarmed but localMachineId is present, build a minimal observation-only fleet (fleetPeers + null hub + observationOnly:true). Zero serving peers → nothing mounts → byte-identical (H3).
  • observationOnlyStagingReceipt (fleet_staging.ts): honest entitlement:"absent", staged:false receipt (no forgery).

Scope / not in scope

  • Observation (read) only — attaches no FleetPlane, hub proxy, or multiplex; adds no control authority.
  • AC4 deferred (post-boot peer pickup without restart): the mount decision stays boot-time to preserve the AC2/H3 byte-identity guard — noted as a #1524 follow-up: in index.ts. Not blocking JJ's case: the roster persists, so a restart with the Studio already serving mounts the route.
  • Does not touch config or turn the server into a fleet client of its own hub.
  • Sibling: the UI half (Fleet Studio: titlebar Sessions dropdown must merge peer sessions from the fleet projection (badge + owner-routed control) #1525) — the titlebar dropdown must merge this projection — is a separate PR.

Gates

  • Fast suite 286 files / 4828 tests / 0 fail (+6 new)
  • Additive-invariants gate GREEN (H3)
  • Typecheck clean
  • Boot-smoke PASS

ACs (tests in amicode_service_fleet_data_plane.test.ts, seam + real wiring)

  • AC1 observationOnly + serving peer + no hub-activation → /sessions + /status 200
  • AC2 zero serving peers → byte-identical to base (routes 404, H3)
  • AC3 serving peer + valid token → session tagged amicode_owner{is_local:false}
  • AC5 resolvable entitlement + lawful overlay → still no premium plane (receipt absent/not-staged, mode engine)

…tivation

An unarmed machine (no amicode.fleetHubUrl/fleetTunnelAlias) that holds a valid
fleet-peer roster + reader tokens could not observe its peers:
GET /amicode/fleet/sessions 404'd because opts.fleet was built ONLY inside the
armed block (amicode_service_wiring.ts:211) and baseStudioActivates (#1478) was
nested inside the premium staging gate.

Fix: an explicit `observationOnly` fleet signal. createAmicodeService BYPASSES
stageFleetDataPlane / the premium plane when set (AC5 — no premium plane stages
even with a resolvable entitlement) and consults ONLY baseStudioActivates to
mount the read/observation routes (/amicode/fleet/status + /sessions). The
wiring builds a minimal observation-only fleet (fleetPeers + null hub) when
activation is UNARMED but localMachineId is present; zero serving peers →
nothing mounts → byte-identical (H3).

AC1/AC2/AC3/AC5 covered at both the createAmicodeService seam and the real
wiring path. AC4 (post-boot peer pickup without restart) deliberately deferred:
the mount decision stays boot-time to preserve the AC2/H3 byte-identity guard;
recorded as a #1524 follow-up rather than forced.

Gates: fast suite 286/4828/0, additive-invariants GREEN, typecheck clean,
boot-smoke PASS.
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 8580296c-fd1b-4ae2-a8e2-1ab2f8a012f4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jeonghun-jj-lee
jeonghun-jj-lee marked this pull request as ready for review September 24, 2026 13:31
@jeonghun-jj-lee
jeonghun-jj-lee merged commit 007a269 into feature/free-tier-fleet Sep 24, 2026
12 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the opencode/fleet-studio-1524-observe-without-hub-activation branch September 24, 2026 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant