Skip to content

feat: the ports of a machine: /v1/ports, /hub/ports, doctor (ADR-0082) - #396

Merged
7schmiede merged 1 commit into
mainfrom
feat/port-model
Oct 30, 2026
Merged

7schmiede merged 1 commit into
mainfrom
feat/port-model

Conversation

@7schmiede

Copy link
Copy Markdown
Member

The first of three pull requests for review card C, the ports of a machine (ADR-0082). It builds point 1. The graph (point 2) and the port shift (points 3–7, #387) follow.

What it adds

  • GET /v1/ports (daemon). It lists every port the stack holds on this machine, per lane: {"ports": [PortEntry]} with port, proto, lane (main or the lane's name), kind, holder?, via, target?, target_lane?, node? and listening.
    • kind is one of: fixed (behind its proxy or forward, and the lane it leads to), instance, published (a container's), held (main_ports), or node (the forward of a lane on a node).
    • The main daemon includes its lanes' ports; /v1/lanes/<lane>/v1/ports lists one lane's.
    • listening comes from devstack's own state, not from a probe.
  • GET /hub/ports (hub). For each registered stack it asks the daemon if one runs, or else reads what the stack file declares for the main tree (kind: "declared": the services' port, announced_ports, and local urls). It answers with {stacks, overlaps}.
    • An overlap is one port and protocol used by more than one stack.
    • Instance ports do not count: the OS hands them out, and two running processes never share one.
    • Each daemon gets a 5 s timeout, the daemons are asked in parallel, and nothing is polled.
  • devstack doctor warns of each overlap (or says there is none). It uses the hub when it runs and reads the daemons and stack files directly otherwise.
  • Compose: a container's ports carry their protocol (compose ps).
  • Mock hub: both routes, with one overlap (the stopped docs stack declares 4200, which shop's web holds). The types are in types.ts; there is no view yet.
  • Docs: ADR-0082 (the whole card, with Built listing point 1), dashboard-api.md, the changelog.

Review card (ADR-0070)

Approved by the owner in the conversation on 2026-10-30, points 1 to 7:

# Category Point Beats Reversing costs
1 contract A daemon reports every port its stack holds on this machine, per lane (GET /v1/ports); the hub joins every stack's (GET /hub/ports) and marks overlaps. Port knowledge scattered in daemon internals; collisions found as a bind failure at start Low: additive
2 concept The stack page shows the port world as a graph: fixed ports → the lane serving them → its services and containers; lanes on nodes with their forwards; overlaps in red. A table or no view Low: UI
3 concept A stack can run on this machine with a port shift: everything a lane offset moves moves by it; Compose project, volumes and data stay. Editing ports in every repository by hand Medium
4 contract The shift is machine-local, in the registry entry, never in the stack file. [stack] port_offset, which moves the collision Low
5 contract Every daemon writes its claimed ports into its registry entry; every allocation avoids all other registered stacks' claims, stopped ones too. The OS probe only Low to medium
6 term Nobody moves silently: up colliding with a running stack starts nothing and offers `--shift auto ` / a button; a stopped stack's collision only warns. Shifting automatically
7 concept A chosen shift sticks until --shift none. Recomputing per start Low

Decided by the agent alone, for the whole card: the shift is a multiple of 1000 (auto takes the lowest one free of every claim, lanes 1–3 included); doctor lists overlaps; a shifted stack's lanes offset from the shifted ports; hard-coded ports do not follow (documented); entries whose folder or stack file is gone are no claim; the stack file does not change.

Decided for this PR:

  • kind and via are plain strings, passed on as they come.
  • listening comes from devstack's state, not from a probe.
  • A stopped service's port is listed with listening: false.
  • Declared ports do not read Compose files' other published ports, since that needs docker compose config; documented.
  • A running daemon too old for /v1/ports gets its declared ports.
  • Instance ports are no overlap.

Tests

  • cargo fmt and cargo clippy --all-targets -D warnings are clean.
  • devstack-daemon: 179 passed (new: the entry builders, overlaps, declared ports, a lane supervisor's ports). devstack-hub: 99 passed. CLI: 51 passed. devstack-compose: 20 passed.
  • a_worktree_runs_as_a_lane (native Windows, --include-ignored) checks three things: the main tree's fixed ports (api via proxy, web via forward), the lane's moved ports, and that /v1/lanes/<lane>/v1/ports lists that lane's alone.
  • UI: npm run typecheck is clean; npm test 115 passed.
  • devstack doctor on this machine: "no two stacks on this machine share one".

🤖 Generated with Claude Code

The first of three steps of review card C. A daemon reports every port
its stack holds on this machine, per lane: fixed ports behind their proxy
or forward and the lane they lead to, instance ports, containers'
published and held ports, and the forwards of lanes on nodes. The hub
joins every registered stack's, a stopped one's as its stack file
declares them, and lists where two stacks meet; `devstack doctor` warns
of each overlap. The mock hub answers both routes. ADR-0082 records the
whole card; the graph and the port shift follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@7schmiede
7schmiede merged commit 3f41162 into main Oct 30, 2026
13 checks passed
@7schmiede
7schmiede deleted the feat/port-model branch October 30, 2026 23:00
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