Skip to content

[kafka pr-1 · 03/6] Slice 03 — Default-private KAs with --public override#392

Open
zsculac wants to merge 12 commits intofeat/kafka-walking-skeletonfrom
feat/kafka-default-private
Open

[kafka pr-1 · 03/6] Slice 03 — Default-private KAs with --public override#392
zsculac wants to merge 12 commits intofeat/kafka-walking-skeletonfrom
feat/kafka-default-private

Conversation

@zsculac
Copy link
Copy Markdown
Contributor

@zsculac zsculac commented May 4, 2026

Stack position

Sub-PR stacking on the kafka foundation branch (PR #390).

```
main
└── feat/kafka-walking-skeleton (foundation rollup, PR #390 — draft)
└── feat/kafka-default-private ← THIS PR
```

Independent siblings (no merge order requirement):

  • Slice 02 — Explicit local-vs-shared CG choice
  • Slice 04 — Opportunistic Kafka probe

Mechanical rebase conflict in `routes/kafka.ts` after a sibling lands.

What slice 03 adds

Endpoint KAs default to `private: true`. Override with `--public` /
`"private": false`. Implements the default-private half of ADR-0004.

The kafka package itself stays privacy-agnostic — the boolean lives entirely
in the route adapter. `KafkaEndpointPublisher.publish` still receives the
bare KA from slice-01; the adapter chooses `{public: ka}` vs `{private: ka}`
envelope before calling `agent.publish`. `packages/kafka/src/` diff against
the foundation should be empty or near-empty.

  • Route accepts optional `private` field (default `true`)
  • API client + CLI gain `--public` flag (no `--private` — default already private)
  • Response echoes resolved `private`
  • Slice-01 tests updated to assert new default (was `{public: ka}`, now `{private: ka}`)

Test plan

  • Route adapter unit: maps `private: true|false|undefined` to correct envelope
  • CLI smoke: `--public` flag, default-omitted behavior
  • e2e: default register + non-participant SPARQL → encrypted/no-cleartext
  • e2e: `--public` register + standard SPARQL → cleartext returned
  • `git diff feat/kafka-walking-skeleton..HEAD -- packages/kafka/src/` is empty or trivial (boundary check)
  • Coverage ratchet updated

Related

  • ADR-0004 (the default-private half)
  • Issue: `.scratch/kafka-registry/issues/03-default-private-kas.md`

Zvonimir and others added 11 commits May 4, 2026 17:15
…pter

Route adapter at packages/cli/src/daemon/routes/kafka.ts now resolves the
privacy posture (default: private: true) from the request body and wraps
the bare KA in either {private: KA} or {public: KA} before calling
agent.publish(). The response echoes the resolved `private` boolean.
New daemon-routes-kafka.test.ts unit-tests all three envelope-selection cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- api-client.registerKafkaEndpoint accepts optional private?: boolean and
  returns private: boolean in the response
- CLI adds --public flag (default is private; --public flips to private: false)
- CLI stdout now prints 'Private: <boolean>' for unambiguous confirmation
- Smoke tests updated: assert default omits private field (route defaults to
  private), --public sends private: false, and stdout echoes resolved value

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… test

Split the single e2e scenario into:
- public (--public): asserts stdout shows 'Private: false' and SPARQL returns KA
- private (default): asserts stdout shows 'Private: true' and HTTP 200; SPARQL
  behavior for encrypted KAs is documented as out-of-scope for single-node
  devnet (encryption-to-CG-participants requires multi-node setup)

Both tests are gated on DKG_KAFKA_E2E=1 and skip by default in CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…onse shape

The api-client.test.ts mock for registerKafkaEndpoint omitted the `private`
field from the response body. Since the api-client return type now declares
`private: boolean` as required, the mock no longer matched the real wire
shape. Adding `private: true` (the default-private posture matching this
test's omitted-field request) keeps the fixture in sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…leniency

Previously the route silently coerced non-boolean values for `private`
(e.g. the string "false") to private via the `!== false` predicate. Now
the route explicitly rejects non-booleans with 400, matching the strict
type-checking already applied to broker/topic/messageFormat in the same
handler. The remaining `!== false` predicate is preserved for the
omitted-defaults-to-private semantic, with a comment warning future
readers not to "tighten" it to `=== true` (which would silently break
the default).

Adds a rejection-case unit test in daemon-routes-kafka.test.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The kafka route had a local `isNonEmptyString` guard that future routes
(slice 02, slice 07) will need. Moves it to http-utils.ts next to the
other validators so the wider daemon can reuse a single guard rather
than copy-pasting the predicate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The kafka route adapter built the { public }/{ private } envelope inline
with an `as Record<string, unknown>` cast. The same pattern recurs in
epcis.ts and is the load-bearing publish-time decision for slice 07's
subscription work. Extracts the wrap into a small typed helper so:

- The cast at the call site disappears (the helper returns JsonLdContent,
  which is exactly what DKGAgent.publish() accepts).
- Future routes adopt the helper instead of re-deriving envelope shape.
- The privacy semantics live in one documented location.

Adds JsonLdContent / JsonLdDocument re-exports to the agent package
barrel so consumers don't reach into deep import paths. Adds a 4-case
unit test for the helper. epcis.ts is intentionally left untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Slice 02's useLocalCg flag and slice 07's subscription privacy flag both
need the same strict-boolean-or-undefined validation that kafka.ts shipped
inline in slice 03. Promotes the validator into http-utils so future
routes adopt one helper instead of copy-pasting the predicate. The
"safe failure mode" comment on the !== false predicate is preserved
because that semantic isn't captured by the validator's docstring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Slices 02 (local-vs-shared CG) and 07 (subscription) both touch the
kafka route and will need exactly the same in-process mocks for
IncomingMessage / ServerResponse / RequestContext that slice 03 wrote.
Extracting now establishes the shared module before those tickets land.

makeFakeRequest gains optional method/url overrides; makeRequestContext
generalizes the agent-publish capture so callers can either rely on the
default capturing mock or supply an onPublish hook to drive custom
behavior. The 6 existing kafka route tests are unchanged in semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The makeRequestContext docstring did not capture the load-bearing detail
that publish capture is unconditional — supplying onPublish drives the
return value to the route handler but does NOT silence publishCalls.
Future test authors needed this contract spelled out.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trim multi-line JSDoc that re-stated what function names and signatures
already say. Keep only load-bearing rationale: the "do not write a
response" half-line on isNonEmptyString (distinguishes from validate*
helpers), the "do NOT tighten to === true" warning on the privacy
predicate, the next-tick emit detail on makeFakeRequest, and the
publishCalls/onPublish contract on makeRequestContext.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread packages/cli/src/daemon/routes/kafka.ts
Comment thread packages/cli/src/cli.ts Outdated
Comment thread packages/cli/src/api-client.ts
…--public help

The walking-skeleton e2e test explicitly notes that the participant-encryption
behavior of V10's `{private: ...}` envelope was out of scope to verify on a
single-node devnet. The simpler "default: private" tells the user the only
thing they need to know to use the flag correctly; ADR-0004 carries the
semantic detail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread packages/cli/src/daemon/routes/kafka.ts
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