Repository navigation
fix: honor native endpoint hosts in generated listeners - #210
Conversation
|
lazybox has finished working on this. claim detail |
Independent post-merge review — what landed is sound; consumer obligations are not dischargedFresh-context review under the The work itself holds upThe Connect JSON fix is real and was reproduced by reversal: Vanguard v0.4.0's default The listener-host fallbacks are correct in source and in test — 9/9 runtime contexts pass, and the reversal fails 5. An empty or unbindable host fails loudly. Obligations that are not written down1. Nothing in the deployed compositions actually has this change. 2. A raw-JSON consumer the PR never names is affected. 3. The native process registry moved 4. 5. The container→native case fails silently at the caller. Core's own Linux runner maps Cleared, not a finding
GO-2026-4883/4887 are Moby daemon-side; only What was runFull root The question this leaves openWho decides, and where is it recorded, that every OBIN module's Connect JSON wire shape changes on its next agent sync? That decision currently exists only in this PR body and one module's service-file comment. There is no OBIN-side record of which faces adopted it, which consumers were checked, or what the platform override should be — so each module adopts blind. Related: codefly-dev/service-go#159 still pins Core v0.19.9, while this carrier took v0.19.10. The v0.19.9→v0.19.10 diff is the Linux fix that stops a live owner being reported absent, which is the predicate orphan recovery kills on. |
Three owner defects in the generated scaffold this agent owns, carried together because they share the generated adapters, the canonical
base/lock and one qualification of the built agent. Consumers adopt all of it through normal Sync.1. Listeners discarded the resolved endpoint host
Generated gRPC, REST and Connect listeners discarded the hostname resolved by Codefly and listened on
:port. A native service assigned a loopback endpoint therefore also accepted connections on other host interfaces.The generated entrypoint carries the resolved endpoint host into the adapters for native/Nix execution. Listeners use
net.JoinHostPort, and gateway/Connect self-dials follow that same address. Container/Kubernetes execution and absent runtime-context legacy behavior retain wildcard binding. Runtime context comes from the existing SDK carrier and Core parser. Existing loopback peer checks, no-proxy dialing and service interceptor chains are preserved. Main-only Sync for service-owned custom adapters retains its existing port-only contract and emits a warning; it does not assume new fields in handwritten adapters.2. Native ownership was not bound to the authenticated launch identity
This carrier adopts Core's authenticated native workload writer and adds a maintained check of the actual built agent. Runtime registration, birth inspection and signals remain Core-owned; orphan selection and recovery remain CLI-owned. The test adapts service-go's proof to go-grpc
Load-reported endpoints and Core network allocation.An independent review found the first version of that test selected its victim by re-reading
ProcessInfo().PIDimmediately beforeSignalProcess, without comparing it to a full identity captured at the original authenticated launch — Core binds the supplied identity to a kernel handle but cannot prove it is the agent the test started. The corrected test reads the manager-launched agent's Core tracked-group handle before any workload RPC (LookupProcessGroup/InspectOwnership), retains the recorded leader identity, and re-inspects that original PID immediately before the death effect, returning on any error or full-identity mismatch. A namedstale_owner_refuses_deliverynegative supplies a changed StartID and requires the pre-signal refusal. The recovery receipt's PID/BootID/StartID/Executable must match the retained owner (its documented owner PGID=0 is checked separately), and the sibling's final identity must equal its original authenticated capture.The
corepin moves tov0.19.10in bothgo.mods and in the reusable-workflow ref, together, and the source builder selectscodefly.dev/go:0.0.74.agent.codefly.yaml'sversionis untouched — that moves at release.3. Connect JSON emitted nulls and defaults the protobuf profile omits
Core v0.19.10's
runnablecontract makes default and unset fields optional (runnable/project.go:126-145, withproject_test.go:281-333establishing the wire shape underprotojson.MarshalOptions{}). The generated Connect bridge contradicted it: an absent message or timestamp came back aschild:null/createdAt:null, with zero scalars and empty lists and maps materialized, so a consumer resolving "none of these" could not return an empty object.Both generated Connect constructors now build the transcoder's codec through Vanguard v0.4.0's existing
WithCodec+NewJSONCodec, withEmitUnpopulated,EmitDefaultValues,UseProtoNames,UseEnumNumbersandAllowPartialexplicitly false; the codec's existing resolver andDiscardUnknownremain intact. Both target protobuf through the existingWithDefaultServiceOptions/WithTargetCodecsseam, and the dialled constructor no longer advertises JSON on the strength of a globally registered gRPC JSON codec — Vanguard would otherwise hand JSON bytes to its opaque protobuf forwarder. One small unexported option factory is shared by the two constructors. Present empty messages stay{}.The promise is Connect JSON. The Connect-only self-hop's native HTTP/2 gRPC direct dispatch (
connect_gen.go:77-86, which prevents recursive transcoding) and the separate REST gateway keep their own output; the READMEs say so.Reuse and classification
All three are fixes at the owner that owns the behavior — no hack, no second mechanism, no consumer-side patch.
Reused: endpoint resolution and the SDK runtime-context carrier; Core's runtime-context classification,
StartTrackedProcessGroup,LookupProcessGroup,InspectOwnershipand identity-boundSignalProcess; the CLI's existing orphan preview/recovery receipt; Vanguard'sWithCodec,NewJSONCodecandWithTargetCodecs; the existing prepared-GOWORKsource-builder seam; the staged transactional Sync.Deliberately not added: no new serializer or parser, no public option framework, no process-global production codec, no address resolver or proxy, no authority bypass, no registry, no alternate service implementation, no consumer schema workaround, and no backward-compatibility switch (explicitly excluded). Factory templates, canonical
base/and their documentation move in the same commits, under the existing parity locks.Docs updated in this PR
docs/native-ownership.md(new — artifact selection, isolation, reproduction, limits),base/code/pkg/adapters/README.mdandtemplates/factory/code/pkg/adapters/README.md.tmpl(the Connect JSON profile, and what REST and native direct-dispatch JSON do instead),AGENTS.md(the reusable CI pin).Validation
a3e353a: fullgo test ./... -count=1 -timeout 8mpassed, including Docker and native lifecycle tests;go build ./...,go vet ./...andgo mod tidy -diffpassed. Process-level entrypoint tests cover native/Nix, container/Kubernetes, absent/blank/unknown contexts, actual loopback versus non-loopback reachability, stop/rebind, Connect-only faces and custom-adapter compilation. Restoring the old wildcard statements makes the reachability regression fail. Built throughcodefly agent build --native-only, then synced two real consumers throughsync service --local-agents: Documents and Robin hot-reloaded the generated source and now bind loopback, with normal authenticated read/chat/citation requests still succeeding while unauthorized and cross-tenant requests fail.4b5673d:TestNativeAgentOwnershipLifecyclePASS, package 1.035s, against an agent freshly built from those exact bytes through the maintained owner command with a private OSHOMEandCODEFLY_HOME(binary SHA256469896db…, adjacent CycloneDX SBOM3ad7a4d8…matching the executable digest and listing Core v0.19.10; metadata clean4b5673d, darwin/arm64, CGO enabled). Covers stale-owner refusal, the actual authenticated-v2 writer, ordinary gRPCLoad/Init/Start/Stop/restart, the identity-bound kill of the disposable owner, the unchanged CLI preview receipt admitted through--expectwithgroup_dead=trueandrecord_removed=true, and the independent sibling leader preserved and then stopped normally.ed59cf3: the newbase/code/pkg/adapters/connect_json_test.gobuilds a reflected protobuf descriptor withdynamicpb, registers a real unary gRPC method and exercisesNewConnectServerover real loopback HTTP in three shapes — in-process, separate dialled gRPC and Connect-only dialled self-hop. It compares raw bytes decoded withencoding/json, never through typed protobuf decoding, which would hide absence and null. Cases: absent messages/timestamps, present empty parent, populated parent with absent children, implicit scalar defaults, empty map and list, custom JSON names, named enum, quoted signed/unsigned 64-bit extremes, Timestamp/Duration/Any resolution, explicitValuenull, absent and present-zero wrappers, and unknown request-field discard. On the parent's production source the same test fails all three shapes.EmitUnpopulated=truefails the absent/nested/default cases in all three shapes; restoring only the JSON target advertisement emits upstream null/default JSON in-process and gives both dialled shapes HTTP 500 invalid protobuf wire-format data, with the registered codec reporting 14 invocations. A separate isolated subprocess registers a real emitting gRPC JSON codec and reruns the suite: it passes with zero calls into that codec, so the profile cannot be bypassed from the global registry, and the parent process registry stays uncontaminated.ed59cf3: 16 selected owner gates pass in 43.286s — factory/base adapters, docs and dependency locks, source message-bound/timeout/dispatch assertions, self-dial address assertions, the fullbase/codesuite (31.33s, throughTestBaseGeneratedServiceServesConfiguredRPCsOverConnect, covering listener host/reachability, transport policy, gRPC/Connect/gRPC-Web, limits, timeout, streaming, REST and shutdown) and the normally rendered Connect-only factory suite (11.78s).GOWORK=offgenerated-servicego build -mod=readonly,basevet and the README parity lock pass.ed59cf3:agent build --native-only --local-agentspassed from cleaned59cf3withGOWORK=off, no ancestorgo.workand privateHOME/CODEFLY_HOME; binary SHA2566870014b…, SBOM358707345…matching the executable digest and identifying Core v0.19.10; metadata darwin/arm64,CGO_ENABLED=1,vcs.modified=false, exact revision.ed59cf3, rehashed all 142 archived source files againstgit show ed59cf3:<path>with no difference, and confirmed design acceptance.Limits — what is not qualified
CreateToRun/matrix and cross-packaging were last run green ata3e353a; they were not rerun oned59cf3(engine allocation belongs to another task). Opt-in packaging-image coverage (CODEFLY_PACKAGING_IMAGE_TEST=1) was not run at all.update service --agent-versionthensync service, never a hand-edited adapter, and its "none of these" empty-response behavior must then be verified against the normally derived contract. That acceptance is not claimed here.0.1.69installation key on the local binary is a candidate, not a release; no release/OCI qualification is implied.🤖 Generated with Claude Code