Repository navigation
feat: Programme B — server siblings machine surface - #69
Merged
Merged
Conversation
…one command InspectSiblings fetches, in one remote command, every named container's published port binds (docker inspect's NetworkSettings.Ports: host_ip, host_port, container_port, proto; loopback/tailnet binds kept as-is, exposed-but-unpublished ports yield nothing) and every named image's config labels + repo tags, for the Programme B siblings command. Both halves are read-only inspects. Objects docker cannot find are skipped (the ResolveImageTags tolerance); containers with no binds still get their key, so 'inspect answered' stays distinguishable from 'inspect never mentioned it' — the caller's wholesale-failure tripwire depends on it. ID-form refs (docker ps's 12-hex) resolve to the image's facts by prefix, name/tag refs by their own RepoTags entry. Port binds are sorted deterministically (the port map is a Go map on the wire).
…y products and forgejo (siblings-v1) `teploy server siblings <server> [--json]`, addressed like server status/audit (servers.yml name or host, --host/--user/--key override). Programme B's discovery read, plan section 10: candidates from what the containers and their images SAY, never from probing. - product: the container's dev.teploy.product label (source label), else the image's (image_label), else — for the forgejo family, whose upstream images carry no teploy label — the image's org.opencontainers.image. title/source naming forgejo/forgejo or gitea (oci_label). Substring matching is the rule there on purpose: it is a candidate for the consumer to probe (forgejo answers /api/v1/version), never a confirmed identity. - a dev.teploy.product value this CLI does not recognise passes through verbatim under unknown_labeled — a product newer than the CLI stays visible; Dash decides what it knows. - version hints: dev.teploy.version, else the teploy release label when the container belongs to a teploy app, else the image's org.opencontainers.image.version. app: the teploy app label. image: the ps ref, with a 12-hex ID resolved to its first repo tag. - port_hints: the container's published binds — where /.well-known/teploy.json may live. No DNS, no HTTP, no URL guessing, no env, nothing secret-adjacent. At most two remote round trips: the shared batched docker ps (labels ride along) plus one batched inspect for binds + image labels. Read-only pinned by test after TestServerAudit_EveryCommandOnlyReads: no mutating verb or docker subcommand, no stdin, no uploads, <=2 commands. An unlistable server or a wholesale inspect failure is state partial/unreachable with empty arrays — never a clean 'no siblings'. Capability token siblings-v1 (the regenerated mi2.json lands here with the code that added it).
Schema derived from the serverSiblingsDTO encoder; fixtures GENERATED from the real collectServerSiblings over a mock executor (the corpus rule — never hand-authored where a producer exists): 'mixed' carries one of each detection class (observe by container label across web+worker, dash by image label, forgejo by OCI labels, an unrecognised product passed through verbatim) and 'none' a server with nothing on it. MANIFEST rev 20 and the artifact table row; both fixtures validated against the schema.
README: a Sibling discovery section (what the labels are, what a row carries, what deliberately is NOT there — probing, DNS, credentials — and the state vocabulary) plus the command-list entry. CHANGELOG Unreleased. AUDIT_OPEN: the new slice's deliberate report-only boundary, the forgejo substring trade-off, the one-row-per-container decision, the every-binds read, and the not-yet-live caveat.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Programme B — Siblings (teploy-cli's slice; plan
_internal/DASH_AUTOCONFIG_PLAN_2026-11-03.mdsection 10, owner-approved). Branch from main6ffdb60(v0.1.53).What
teploy server siblings <server> --json— the read-only discovery read Dash runs before offering to connect the products that share a server. Addressed likeserver status/server audit(servers.yml name or raw host; global--host/--user/--keyoverride).Envelope (
contracts/schema/siblings-envelope.schema.json, corpus rev 20):{machine_interface, server, host, observed_at, state, error?, siblings[], unknown_labeled[]}; each row{container, product, version?, app?, state, image, port_hints[], source}.dev.teploy.productlabel (source: label), else the image's (image_label), else the forgejo family by the image's OCI labels (org.opencontainers.image.title/sourcenaming forgejo/forgejo or gitea →product: forgejo,source: oci_label).dev.teploy.productvalue passes through verbatim underunknown_labeled; Dash decides what it knows.dev.teploy.version, elseteploy.versionwhen the container belongs to a teploy app, else the image'sorg.opencontainers.image.version; app — the teploy app label; image — the ps ref with a 12-hex ID resolved to its repo tag./.well-known/teploy.jsonmay live. The command OBSERVES only: no DNS, no HTTP, no URL guessing, no env, nothing secret-adjacent. Credential minting / env writes need a confirmedinstance_id+ admin consent — consumer's surface.ok | partial | unreachable— an unlistable server or a wholesale inspect failure is partial with empty arrays, never a clean "no siblings".--jsonexits 0 whenever the envelope was produced.Bounded + read-only
At most two SSH round trips: the shared batched
docker ps(labels already ride along viacontainersFormat) plus one batched inspect (docker.InspectSiblings) fetching every container's port binds and every distinct image's labels + tags in a single command — image-label candidates get their hints without a third trip.TestServerSiblings_EveryCommandOnlyReads(after the audit pin) refuses any mutating verb/docker subcommand/stdin/upload and pins ≤ 2 commands.Capability + contracts
siblings-v1in the registry andversion --json(regeneratedmi2.json).siblings-envelopeschema derived from the DTO; fixtures generated from the realcollectServerSiblingsover a mock executor:mixed(one of each detection class + pass-through) andnone; both validated against the schema. MANIFEST rev 20.Commits
feat(docker): batched sibling inspect (port binds + image labels, one command)feat(siblings): the command, detection fallbacks, capability tokencontracts: siblings-envelope, corpus rev 20docs: README sibling-discovery section, CHANGELOG, AUDIT_OPENVerification
go build ./...,go vet ./...,GOOS=linux go vet ./...,go test ./...all green; gofmt clean on touched files;-raceon the touched packages; every intermediate commit builds and tests green. Hermetic tests cover: all detection sources and fallbacks, container-label-beats-image, version priority, port-hint extraction (dual-stack, loopback, tailnet, udp, exposed-but-unpublished), unknown-product pass-through, ID-created image tag resolution, determinism, JSON round-trip, partial vs ok states, and the read-only pin. Deferred items and the forgejo substring trade-off recorded inAUDIT_OPEN.md.