Skip to content

fix(api): runtime settings, recorder, logs and datapath (C4) - #39

Merged
Zakkaus merged 5 commits into
daeuniverse:honkfrom
Zakkaus:fix/contract-runtime-settings
Sep 28, 2026
Merged

Zakkaus merged 5 commits into
daeuniverse:honkfrom
Zakkaus:fix/contract-runtime-settings

Conversation

@Zakkaus

@Zakkaus Zakkaus commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Contract PR C4 of the API revision plan: runtime settings, recorder, logs and datapath (F8, F14, F15, F17, F18, F39, F45). Plan decision Q7 applies.

Five commits: runtime settings and recorder; DNS capacity, log filters and eBPF attachments; flow-step actions; review fixes (recorder policy, grace scope, partial attachments, DNS ratio, retention floor, logs 422); wording. Rebased on honk after C3 (#38). Schema tests cover each new conditional requirement.

F15, Q7: runtime settings sections are optional

  • Change: RuntimeSettings requires only observed_at and source. A value appears when resources.runtime_settings.fields lists it; an engine may also report values it cannot change. Inside log, level and buffered_records are independent, so {"log": {"level": "info"}} is a complete PATCH and a valid GET body (new level_only examples). The fixed 64-record minimums leave the schema (minimum 1); engines advertise logs.min_buffered_records, dns_log.min_records and flows.min_flows, absent meaning 1. An empty section is still invalid.
  • Retention has no advertised minimum: resources.flows.retention_seconds is now at least 1 and a PATCH may always set 1, the same fixed floor the settings table already stated. This is the smaller change; the rings keep their min_* fields because 64 is honk's floor, while every engine can retain for 1 second.
  • honk: matches for the optional sections (honk reports every section) and the retention floor (PATCH accepts 1..=300). HC: advertise min_buffered_records: 64, min_records: 64 and min_flows: 64, which honk's PATCH enforces (settings.rs patch_with).
  • dae: an adapter lists only log.level in fields, reports {"log": {"level": ...}}, and applies a PATCH with logger.SetLogger/SetLevel as the reload worker already does (cmd/run_reload_worker.go:115-124). No ring, flow or recorder fields.

F14, Q7: recorder state is {allowed, mode, active} plus on_demand

  • Change: RecorderState keeps allowed, mode and active. auto now means "record on demand"; what counts as demand and how long it lasts is engine-defined. Inside recording, each recorder appears when its record_* field is listed; events is optional. honk's attachment and flow-demand rules (60-second grace, which requests renew it) move to the new "Runtime settings in honk" section of the honk notes.
  • resources.flows.recording reports policy, not current capture: off when the recorder is not allowed or its mode is off, on when the mode is on, on_demand when the mode is auto whether or not a client creates demand now. recording.flows.active reports capture.
  • RecorderMode is the PATCH value (true, false, "auto"); RecorderState.mode is the GET string (on, off, auto). Both descriptions and the PATCH section now say so.
  • grace_remaining_seconds is the engine's attachment grace only, optional; which recorders follow it is engine-defined and a recorder may keep its own demand timer. No per-recorder grace.
  • honk: state object and grace match (settings.rs:240-243 reports the attachment timer only). HC: report the policy: on_demand in auto mode, on when pinned, off when not allowed or pinned off (today types.rs capabilities reports on/off from the active flag).
  • dae: no recorders; the adapter lists no record_* field, omits recording and grace_remaining_seconds, and reports flows unavailable or recording: off.

F8: flow capability ceilings are the engine's limits

  • Change: resources.flows.max_flows and retention_seconds are described as the most a runtime-settings PATCH may set, not the current values (discovery schema, runtime-status and flows pages).
  • honk: HC: report 1024 and 300 (types.rs capabilities uses settings.flow_limits(), the current values). logs.max_buffered_records and dns_log.max_records already report the 512 maximum.
  • dae: not applicable while dae has no flow recorder; a future recorder reports its compile-time limits.

F17: honk constants are not contract limits

  • Change: DnsCacheUsage.entry_capacity is UInt64 | null (null: no entry limit or unknown) and loses the 100,000 cap. dns-cache.md now ties eviction and the entries / entry_capacity ratio to a positive capacity; a null or zero capacity supports no ratio. operations.max_replay_keys was already advertised in C1; it is the field merged.md F17 calls max_idempotency_keys, and the C1 name stays. The 12-hour session constant moved to the honk notes in C3 (docs: access model, geodata lifecycle and one SSRF policy (C3) #38), so this PR leaves auth.md alone.
  • honk: matches for DNS (reports its configured capacity). HC: advertise operations.max_replay_keys: 1024 (operations.rs MAX_TOMBSTONES).
  • dae: reports the configured cache size, or null when it is 0 or less, which dae treats as unlimited (control/dns_controller_cache.go enforceDnsCacheCapacityLocked).

F18: flow-step actions have a core set and an extension point

  • Change: DatapathStepData.action is pass, redirect, hold, drop, or an engine-defined lowercase token (^[a-z][a-z0-9_]*$, at most 64 characters); clients show an unknown value as it is. The NFQUEUE hold/arm/verdict prose, the UDP decision token, the token allocator and the Clash-mode wording move to "Flow steps in honk". mode_override keeps its values with neutral wording; its schema no longer names honk's Clash mode.
  • honk: matches. honk emits the core drop (control/udp_endpoint/mod.rs:263, retirement.rs:70, control/connection/udp.rs:443,446) and the engine-defined activate_direct and activate_proxy (control/connection/udp.rs:410,472). "Flow steps in honk" lists them as engine-defined values and says honk records the activation, not each hold, arm, verdict and publication step.
  • dae: has no flow recorder yet; its tc and cgroup verdicts map to the core values, and it needs no NFQUEUE value.

F39: log target is nullable and filters are advertised

  • Change: LogRecord.target is string | null. resources.logs.filters (required when available, always contains level) lists the GET /logs filters; target on an engine that does not list it returns 422 unsupported_value, and a target filter never matches a null target. GET /logs now declares that 422 with an example. flows.md, logs.md and capabilities.md list filters and the min_* fields.
  • honk: target values match. HC: advertise filters: [level, target] (logs.rs capability).
  • dae: logrus has no module target, so records carry target: null and the adapter advertises filters: [level].

F45: eBPF attachments are typed, and the list may be partial

  • Change: EbpfAttachment requires kind: interface | cgroup | other. An interface attachment requires interface and direction; a cgroup attachment requires cgroup (v2 path relative to the mount); an other attachment requires a free hook description. Each kind rejects the other kinds' fields. attachments lists what the engine checks and may be partial. The example shows all three.
  • honk: HC: emit kind: interface (datapath.rs Attachment).
  • dae: reports its tc attachments as interface and its cgroup programs (sock_create, connect4, sendmsg4 and the rest, control/control_plane_core_bind.go:305-310) as kind: cgroup, cgroup: "/". Its sockmap verdict program (control/control_plane_core_bind.go:392-407) and fentry/kprobe accounting (control/tcp_offload_hook.go:151-165) are kind: other with a hook description, or are left out of the partial list.

Checks

npx -y yarn@1.22.22 check:contract: bundle, lint and 81 tests pass. New or changed tests: optional settings sections and the level-only request, recorder state and on_demand, advertised minimums, log filters and nullable target, nullable DNS capacity, attachment kind conditionals including other, datapath step actions, the flow retention floor, the logs 422.

The HC rows are recorded in the drift ledger for the honk follow-up.

… engine-defined

Only observed_at and source are required; log.level and the replay ring are
independent, so a level-only engine is valid. Fixed 64-record minimums leave
the schema and engines advertise min_* bounds. Flow capability ceilings are
the engine's limits, flows.recording gains on_demand, and honk's attachment
and demand timing moves to the honk notes.
… attachments

entry_capacity may be null for an unbounded cache and loses the 100,000 cap.
Log target is nullable and resources.logs.filters lists the supported GET
/logs filters. eBPF attachments carry kind interface or cgroup, so cgroup
hooks such as dae's are reportable.
action keeps pass, redirect, hold and drop as the core set and accepts other
lowercase engine values, which clients show as they are. honk's NFQUEUE
actions, UDP decision token and Clash-mode override move to the honk notes.
@Zakkaus
Zakkaus force-pushed the fix/contract-runtime-settings branch from 03b81fd to 2a16334 Compare September 28, 2026 17:40
…ments

- attachments gain an `other` kind with a free `hook`; the list may be partial
- `flows.recording` reports policy (off/on/on_demand), not current capture
- `grace_remaining_seconds` is the engine's attachment grace, optional
- RecorderMode (PATCH) and RecorderState.mode (GET string) documented apart
- DNS eviction and fill ratio only with a positive capacity
- flow capability retention is at least 1 second
- GET /logs declares 422; enumerations list filters and min_* fields
- flow-steps drops the honk Clash-mode example; honk notes list the actions
  honk emits today
@Zakkaus
Zakkaus force-pushed the fix/contract-runtime-settings branch from 2a16334 to e30a67b Compare September 28, 2026 17:40
@Zakkaus
Zakkaus merged commit 74ab2b8 into daeuniverse:honk Sep 28, 2026
1 check passed
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