Skip to content

feat(shared): expose shared services on stable localhost ports for GUI clients - #110

Merged
gustavobertoi merged 1 commit into
mainfrom
feat/shared-expose
Jul 1, 2026
Merged

feat(shared): expose shared services on stable localhost ports for GUI clients#110
gustavobertoi merged 1 commit into
mainfrom
feat/shared-expose

Conversation

@gustavobertoi

Copy link
Copy Markdown
Contributor

Requested from live testing: reach the shared engines (Postgres/MinIO/Redis/…) from host GUI clients like DataGrip, TablePlus, an S3 browser, or the RabbitMQ management UI — without a duplicate stack per repo.

Commands

  • devstack shared expose [services...] — publish the shared engines on stable 127.0.0.1 host ports. No args = every exposable engine; name services to scope it.
  • devstack shared ports — read-only projection: ports + copy-paste connection strings (lock-free).
  • devstack shared expose --off — back to DNS-only.

Design (spec 03 host-reachability)

  • Opt-in, loopback-only. Default stays "no host ports" (DNS over devstack_shared). Exposure is an up-time compose overlay (compose.expose.yaml) — the deterministic, golden-asserted generated compose is untouched (same posture as the provisioning overlay).
  • No-collision by construction. Ports are ledger-allocated (FreeHostPort), stable across runs, in a distinct 5xxxx range so the expose overlay and the 4xxxx provisioning overlay never publish the same host port. Kafka is the one deliberate exception (host clients must use the fixed advertised 127.0.0.1:49092). A unit test enforces the invariant.
  • Persistent. The up saga re-applies the overlay so host ports survive up/down. Refused on a remote backend (a remote bridge isn't host-routable).
  • Useful surface. Per engine it publishes the primary protocol port + secondary UI ports (MinIO console, RabbitMQ management, NATS monitor) and prints a client-ready connection string (the admin DSN + a reminder that per-project DBs use the documented per-project dev creds).

Example

$ devstack shared expose
SERVICE                    PORT      ADDRESS            CONNECT
shared-postgres            postgres  127.0.0.1:55432    postgres://devstack:devstack@127.0.0.1:55432/postgres?sslmode=disable
shared-minio               s3        127.0.0.1:59000    http://127.0.0.1:59000
shared-minio (console)     console   127.0.0.1:59001    http://127.0.0.1:59001

per-project database: postgres://<project>:<project>@127.0.0.1:55432/<project>?sslmode=disable

Tests

  • Unit: overlay write/read round-trip, instance resolution (all/named/rejects non-engines), per-engine connection URLs, the expose-vs-provision no-collision invariant, CLI table/JSON/quiet rendering, command registration.
  • Verified live on WSL2: shared ports projection; the overlay merges cleanly with the running shared compose (docker compose config); a host psql client reaches shared-postgres on the published loopback port (HOST-CONNECT-OK PostgreSQL 18.4).
  • Determinism/golden unchanged; orchestrate + cli suites green.

🤖 Generated with Claude Code

…I clients

`devstack shared expose [services...]` publishes the shared engines on stable
127.0.0.1 host ports so a developer's GUI clients — DataGrip/TablePlus, a Redis
or S3 browser, the RabbitMQ management UI — can connect, without a duplicate
stack per repo. `devstack shared ports` is the read-only projection (ports +
connection strings); `shared expose --off` returns the stack to DNS-only.

Design (spec 03 host-reachability):
- Opt-in and loopback-only; the default is still "no host ports" (DNS over
  devstack_shared). Exposure is an UP-TIME compose overlay (compose.expose.yaml),
  so the deterministic, golden-asserted generated compose is untouched — same
  posture as the provisioning overlay.
- Ports are ledger-allocated (FreeHostPort), stable across runs, and sit in a
  distinct 5xxxx range so the expose overlay and the 4xxxx provisioning overlay
  never publish the same host port (a duplicate binding). Kafka is the one
  deliberate exception: host clients must reach the fixed advertised 127.0.0.1:49092.
- Persistent: the up saga re-applies the expose overlay so host ports survive
  up/down. Refused on a remote backend (a remote bridge is not host-routable).
- Per engine it publishes the primary protocol port plus the useful secondary UI
  ports (MinIO console, RabbitMQ management, NATS monitor) and prints a
  client-ready connection string for each (the postgres admin DSN + a reminder
  that per-project DBs use the documented per-project dev creds).

Tests: overlay write/read round-trip, instance resolution (all/named/rejects
non-engines), per-engine connection URLs, the expose-vs-provision no-collision
invariant, CLI table/JSON/quiet rendering, and command registration. Verified
live on WSL2: `shared ports` projection, the overlay merges cleanly with the
running shared compose (`docker compose config`), and a host psql client reaches
shared-postgres on the published loopback port. Determinism/golden unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gustavobertoi
gustavobertoi merged commit 4ee7a49 into main Jul 1, 2026
4 checks passed
@gustavobertoi
gustavobertoi deleted the feat/shared-expose branch July 1, 2026 18:51
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