Skip to content

chore(release): promote rc-2026.8.3 - #180

Open
jacderida wants to merge 11 commits into
mainfrom
rc-2026.8.3
Open

chore(release): promote rc-2026.8.3#180
jacderida wants to merge 11 commits into
mainfrom
rc-2026.8.3

Conversation

@jacderida

Copy link
Copy Markdown
Member

Promotes rc-2026.8.3 to release version(s): 0.3.4,0.7.0.

  • strips -rc.* from [package].version
  • rewrites internal git+branch deps to crates.io version pins
  • regenerates Cargo.lock

Once merged, the release tag will be pushed to fire the publish workflow.

jacderida and others added 11 commits August 18, 2026 23:19
Beta participants need their node logs in the beta-channel Elasticsearch so the release gates have
evidence to judge a build on, and the only way to collect them today is for each user to hand-roll a
Vector config against their node log directory. That does not scale past the first few technical
users and it is not something we can ask a wider beta cohort to do.

The daemon already supervises every node on the machine and already knows each one's log directory,
so it is the natural place to do this: no Windows service, no MSI, no admin prompt, no systemd unit,
no separate install, and cross-platform for free. `ant node logs forward enable --token <token>` is
the consent act; `disable` stops the flow and changes nothing else about any node.

Two properties drive the design. Forwarding must never slow a node down, so it only ever reads log
files, on its own task, with a bounded drop-oldest queue and bounded retries — a lost batch is
acceptable, a stalled or memory-hungry daemon is not. And a daemon restart must neither duplicate
nor lose events, which is why tail offsets are persisted and every document carries a deterministic
`_id` derived from node, file and byte offset.

That `_id` is coupled to the index name, not independent of it: `_id` uniqueness is per index, so
each document is filed under an index derived from its own `@timestamp` rather than the wall clock.
Deriving the index from the wall clock would send a batch replayed after midnight to a different
daily index, where the duplicate would be silently accepted instead of rejected with a 409.

Notable contract details from V2-1016, all covered by tests:

- the bulk action must be `create`, never `index` — the write key grants `create_doc`, and `index`
  comes back as a per-item 403
- a `_bulk` response is HTTP 200 even when documents failed; success is per position in
  `items[].status`, so trusting the HTTP status alone silently discards failures
- at a position, 201 is created, 200 is dropped by the server-side level filter, and 409 is a
  document our own earlier attempt already landed — all three are successes and none is retried
- `host` and `beta_user` are never sent: the ingest pipeline strips the first (hostnames routinely
  contain personal names) and stamps the second from the authenticated API key

Node file logging remains off by default. `enable` forwards only nodes that already have a log
directory and reports the ones it is skipping, pointing at `--log-dir-path`, rather than appearing
to succeed while shipping nothing.

- Add ant-core/src/node/daemon/forward/: config (0600 token file), line parsing for both the text
  and JSON log layouts, rotation-aware tailing with persisted offsets, document tagging, a bounded
  batching sink with per-position retry, the Elasticsearch bulk sink, and the background task
- Add GET /api/v1/logs/forward and POST .../enable|disable, with OpenAPI paths and schemas
- Add `ant node logs forward enable|disable|status`, dual-path so the opt-in is still recorded when
  the daemon is down
- Update CLAUDE.md and the e2e node management skill

The status API returns a token fingerprint, never the token itself.

Test results:

- cargo test -p ant-core --lib: 562 passed (105 new)
- cargo test -p ant-core --test log_forward_integration: 8 passed, driven against a real HTTP
  endpoint speaking the bulk contract, covering restart resume, replay idempotency, daily rotation,
  multi-line events and index-by-event-date
- daemon_integration 6, node_add_integration 3, datamap_file 20, merkle_unit 8,
  unit_self_encrypt 16, ant-cli 18 — all passed
- cargo clippy --all-targets --all-features -- -D warnings: clean
- cargo fmt --all -- --check: clean

`data::client::adaptive::tests::controller_perf_overhead_is_bounded` fails under a fully parallel
run on a loaded machine. It is pre-existing and unrelated: it fails identically with these tests
excluded, passes in isolation, and adaptive.rs is untouched here.

The beta endpoint is still being provisioned, so a smoke test against the real logs.autonomi.com is
outstanding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Whether forwarding is enabled before or after a node starts changes what gets shipped, and the
difference is not obvious from the code: the join-at-end rule applies only to files that already
existed when forwarding was switched on, so a node that has never run is read from its first byte
while one already running is picked up from wherever it had got to.

That matters more than it looks. ant-node reports its version, commit and peer id on its startup
line, so enabling after the node is up costs those fields on every document in the batch, along
with the bootstrap and listen-address lines that show whether the node actually joined.

Both directions were verified by hand before being written down here; these tests stop a later
change to the priming logic silently reversing either one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Beta participants have nowhere to learn two things they need to know, and both are the kind of
detail that only announces itself once it has already cost someone a run.

The first is that log forwarding is optional. It is opt-in telemetry from people's own machines, so
the docs need to say plainly that running beta builds is the whole requirement and that declining to
forward costs nothing — if it reads as expected, the consent that `enable` is supposed to represent
stops meaning very much. The section is marked optional in its heading and its walkthrough is
conditional on having chosen to turn it on. It still makes the case for saying yes, because an
informed choice needs one, but leaves it a choice.

The second is ordering. Enabling forwarding is forward-looking consent, so for a log file that
already exists the daemon starts reading at the end of it. A node that has not started yet has no
file, so the one it creates is read from the first line — meaning enable-then-start captures the
node's startup line and start-then-enable silently skips it, losing the version, commit and peer id
that say which build produced everything that follows. The same applies to --log-dir-path, which has
to be set when a node is added: node file logging is off by default, and a node added without it
writes nothing to forward. `enable` reports those nodes rather than failing quietly, but the fix is
to re-add the node, so it is much better to get it right first time.

Adds a Beta Programme section covering both, with the working order as a single copyable block and
the reasoning below it, plus what is and is not sent, how to tell if delivery is failing, and how to
stop — keeping turning forwarding off separate from leaving the beta channel, since they are
unrelated actions.

The existing beta channel content was filed under `ant update` although it is not an `ant update`
subcommand; it moves here rather than being duplicated. Also adds the `ant node logs forward`
command reference alongside the other subcommands, the three new REST endpoints to the API table,
and the new modules to the project structure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Installing the beta client meant downloading an archive from the releases page by hand, because the
installers could only ever fetch the newest stable build. That is a poor first step for a programme
whose whole point is getting more people onto the pre-release build, and it is the one part of beta
onboarding with no way around it.

Both installers now take ANT_CHANNEL=stable|beta, defaulting to stable so existing invocations are
unaffected. ANT_VERSION still overrides it.

The version resolution is the substance of this. /releases/latest cannot serve the beta channel at
all: GitHub excludes pre-releases from that endpoint, so it would always return the newest stable
build. Beta scans the release list instead, while stable keeps using /releases/latest, which already
means exactly the right thing.

Picking the highest pre-release from that list would be worse than not implementing this. Semver
ranks -rc above -beta, and both 0.3.4-beta.1 and 0.3.4-rc.1 exist right now, so the naive choice
installs a release candidate — code published before the release gates have reported. Both scripts
therefore mirror version_matches_channel from ant-core/src/channel.rs: final releases on either
channel, -beta.N additionally on beta, everything else rejected, matching the whole first identifier
so `betamax.1` is not caught by a prefix test.

Semver comparison is hand-rolled in both rather than delegated. `sort -V` does not implement the
rule that a pre-release ranks below the release it was cut from, and BSD and GNU builds disagree,
which matters because install.sh runs on macOS as well as Linux. PowerShell's [version] cannot parse
a pre-release suffix at all.

Download URLs and asset names needed no change: ant-cli-v0.3.4-beta.1 already publishes its assets
as ant-{version}-{target}.{tar.gz,zip}, so only resolution differed.

Note that the channel rule now lives in three places — these two scripts and channel.rs — with only
comments binding them together. Worth collapsing if it grows a fourth.

Test evidence:

- 19 logic cases in bash and 14 in PowerShell, both agreeing with channel.rs: -rc rejected on beta,
  betamax rejected, 0.3.4 ranked above 0.3.4-beta.1, 0.10.0 above 0.9.0
- live resolution from both scripts: stable -> 0.3.3, beta -> 0.3.4-beta.1, i.e. beta correctly
  preferred over the higher-ranked 0.3.4-rc.1
- install.sh run end to end against real GitHub releases into a temp prefix: ANT_CHANNEL=beta
  installed a working ant 0.3.4-beta.1, and the default installed ant 0.3.3
- install.ps1 parses clean under pwsh and its helpers were exercised directly; its Windows-only
  install body was not run

Also documents ANT_CHANNEL, ANT_VERSION and INSTALL_DIR in the Installation section, and replaces
the manual-download step in the beta walkthrough.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses both blocking findings from review of 8658e16.

Document ids could collide across installations. The id was built from node id, filename and byte
offset, all of which are local values: every participant has a node 1, writing the same daily
filename, whose first line starts at byte 0. Since the whole cohort writes into one shared
beta-nodes-YYYY.MM.DD index and the sink counts a 409 as delivered, the second machine to send a
given position had its event silently discarded.

This was worse than a possibility. Offset 0 of each day's file is reached by every node on every
machine, so on any given day the first event from node 1 collided across the entire cohort and
exactly one won — and that first event is the startup line carrying version, commit and peer id,
which is precisely the record the forwarding exists to collect.

Ids are now prefixed with a random 64-bit installation namespace, minted on first enable and
persisted alongside the opt-in. It is generated from random bytes rather than derived from hostname,
MAC or username, so it separates installations without describing them, and it is deliberately
stable: regenerating it would make a replayed batch look like new documents and duplicate them,
which is the property the deterministic id exists to provide.

Disabling did not stop delivery that was already under way. Cancellation was only observed between
poll cycles, so a disable issued mid-flush kept uploading through the rest of the retry ladder —
with the default policy, up to three 30s request timeouts plus backoff per batch, repeated for every
batch left in the queue — while the CLI had already told the user forwarding had stopped. For a
feature whose entire basis is opt-in consent, revocation has to mean something more definite.

Cancellation now reaches the delivery loop: checked before taking each batch, and raced against the
delivery itself, so the in-flight future is dropped and the HTTP request cancelled with it. The
handle retains its JoinHandle and `stop_and_wait` awaits the task, so the disable endpoint returns
only once the sender has actually stopped rather than merely having been signalled. Starting a
replacement forwarder awaits the old one for the same reason, so two never overlap.

Tests:

- identical node/file/offset tuples from two installations produce different ids, and an
  integration test drives two forwarders through the shared mock endpoint to show both events are
  stored rather than one being swallowed as a conflict
- the installation id is minted once, survives save/reload, and is unchanged by re-enabling or
  rotating the token
- a blocking sink holds a request open across a disable: stop_and_wait returns promptly, the
  blocked send is confirmed never to have completed, and no request starts afterwards. Reverting
  the mid-delivery cancellation makes this test fail, so it pins the behaviour rather than
  describing it

Full run: 570 lib tests, 9 log-forwarding integration tests, 6 daemon integration, 18 ant-cli, all
passing; clippy -D warnings and fmt --check clean.

Also documents the installation identifier and the disable guarantee in the README's beta section,
since both are things a participant deciding whether to opt in should be told.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat: forward beta node logs from the daemon
Resolves the merge-back conflict for the 0.3.4 / 0.7.0 promotion.

Only README.md conflicted. Both sides added text at the same point,
after the two install commands: `main` gained a note that the
`curl | bash` installer is Linux/macOS only and WSL users want the
PowerShell one (#169), and the rc branch gained the table of
environment variables shared by both installers (#175). They are
complementary, so both are kept — the note first, since it decides
which installer to run, then the table that applies to either.

Everything else merged cleanly, including both manifests and
Cargo.lock. Verified after the merge that the promotion survived it:
ant-core 0.7.0 and ant-cli 0.3.4, ant-protocol pinned to 2.3.3, both
ant-node pins (optional devnet and dev-dependency) to 0.17.2, no
`-rc.`/`-beta.` suffixes and no git+branch sources left in the lock.
`cargo check --all-targets --all-features` passes against the
published crates and left the lock unchanged.

The release tags are unaffected: ant-core-v0.7.0 and ant-cli-v0.3.4
point at 7145a0d, which this merge builds on rather than rewrites.
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