ant-core 0.8.1

Headless Rust library for the Autonomi network: data storage and retrieval with self-encryption and EVM payments, plus node lifecycle management.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
# ant — Autonomi Network Client

A unified CLI and Rust library for storing data on the Autonomi decentralized network and managing Autonomi nodes.

## Overview

This project provides two crates:

- **ant-core** — A headless Rust library containing all business logic: data storage/retrieval with self-encryption and EVM payments, node lifecycle management, and local devnet tooling. Designed to be consumed by any frontend (CLI, GUI, AI agents, REST clients).
- **ant-cli** — A thin CLI binary (`ant`) built on `ant-core`.

Data on Autonomi is **content-addressed**. Files are split into encrypted chunks (via [self-encryption](https://en.wikipedia.org/wiki/Convergent_encryption)), each stored at an XOR address derived from its content. A `DataMap` tracks which chunks belong to a file. Payments for storage are made on an EVM-compatible blockchain (Arbitrum).

## Installation

### Linux / macOS

```bash
curl -fsSL https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.sh | bash
```

### Windows

```powershell
irm https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.ps1 | iex
```

> **Note:** the `curl ... | bash` installer is for Linux/macOS only. Running it inside WSL installs a Linux binary that is only usable from within WSL — to use `ant` from PowerShell or cmd, install with the PowerShell command above.

Both installers take the same environment variables:

| Variable | Description |
|----------|-------------|
| `ANT_CHANNEL` | Release channel: `stable` (default) or `beta`. See [Beta Programme]#beta-programme. |
| `ANT_VERSION` | Install one specific version, e.g. `0.3.3`. Overrides `ANT_CHANNEL`. |
| `INSTALL_DIR` | Where to put the binary. Defaults to `~/.local/bin` on Linux, `/usr/local/bin` on macOS, `%LOCALAPPDATA%\ant\bin` on Windows. |

## Quick Start

### Store and retrieve a file (production)

```bash
# Upload (private — DataMap saved locally)
SECRET_KEY=0x... ant file upload photo.jpg -b 1.2.3.4:12000
# Output:
#   Upload complete!
#     Datamap: photo.datamap
#     Chunks:  3

# Download using the local DataMap
ant file download --datamap photo.datamap -o photo_copy.jpg -b 1.2.3.4:12000
```

### Store and retrieve a file (local devnet)

```bash
# 1. Start a local devnet (spins up 25 nodes + a local Anvil EVM chain)
cargo run --release --example start-local-devnet

# 2. Upload a file (the manifest is auto-written to the shared data dir)
SECRET_KEY=0x... ant file upload photo.jpg --public \
    --devnet-manifest ~/.local/share/ant/devnet-manifest.json --allow-loopback --evm-network local

# 3. Download it back
ant file download abc123... -o photo_copy.jpg \
    --devnet-manifest ~/.local/share/ant/devnet-manifest.json --allow-loopback --evm-network local
```

### Store and retrieve a file (Arbitrum mainnet)

```bash
# Upload (private — DataMap saved locally)
SECRET_KEY=0x... ant file upload photo.jpg \
    --bootstrap 1.2.3.4:12000 --evm-network arbitrum-one
# Output: DATAMAP_FILE=photo.jpg.datamap

# Download using the local DataMap
ant file download --datamap photo.jpg.datamap -o photo_copy.jpg \
    --bootstrap 1.2.3.4:12000 --evm-network arbitrum-one
```

### Low-level chunk operations

```bash
# Store a single chunk (< 1 MB)
echo "hello autonomi" | SECRET_KEY=0x... ant chunk put --bootstrap ...
# Output: abc123def456...

# Retrieve it
ant chunk get abc123def456... --bootstrap ...
# Output: hello autonomi
```

---

## CLI Reference

### Global Flags

| Flag | Description |
|------|-------------|
| `--json` | Output structured JSON instead of human-readable text |
| `-b, --bootstrap <IP:PORT>` | Bootstrap peer addresses, comma-separated or repeated (`-b 1.2.3.4:10000,5.6.7.8:10000`) |
| `--devnet-manifest <PATH>` | Path to devnet manifest JSON file |
| `--allow-loopback` | Allow loopback connections (required for local devnet) |
| `--timeout-secs <N>` | Network operation timeout in seconds (default: 60) |
| `-v, --verbose` | Increase verbosity: `-v` info, `-vv` debug, `-vvv` trace. Default: no logs (privacy by design) |
| `--evm-network <NET>` | EVM network: `arbitrum-one` (default), `arbitrum-sepolia`, or `local` |

### `ant file` — File Operations

Upload and download files with automatic chunking, self-encryption, and EVM payment.

#### `ant file upload <PATH>`

Upload a file to the network. The file is split into encrypted chunks, each paid for via the configured EVM network. Requires `SECRET_KEY` environment variable.

```
$ SECRET_KEY=0x... ant file upload my_data.bin --public
Connecting to network... done
Approving token spend... done
Uploading my_data.bin (439.5 KB)...
Storing public data map... done

Upload complete!
  Address: a1b2c3d4e5f6...
  Chunks:  7
  Size:    439.5 KB
  Time:    12.3s

Anyone can download this file with:
  ant file download a1b2c3d4e5f6...
```

**Options:**

| Flag | Description |
|------|-------------|
| `--public` | Store the DataMap on-network (anyone with the address can download). Without this flag, the DataMap is saved to a local `.datamap` file (private). |
| `--merkle` | Force merkle batch payment (single EVM transaction for all chunks). Reduces gas costs for multi-chunk uploads. |
| `--no-merkle` | Disable merkle, always use per-chunk payments. |

**How it works:**
1. The file is streamed through self-encryption in 8KB reads (never fully loaded into memory).
2. Each encrypted chunk is stored on the network at its XOR content address.
3. Payment is made per-chunk or via a merkle batch transaction (auto-selected by default when >= 64 chunks).
4. A `DataMap` is produced that records which chunks compose the file.
5. In `--public` mode, the DataMap itself is stored as a chunk; the returned address is the DataMap's content address. In private mode, the DataMap is saved to `<filename>.datamap` on disk.

#### `ant file download [ADDRESS]`

Download a file from the network.

```
# Public download (by address)
$ ant file download a1b2c3d4e5f6... -o restored.bin
Connecting to network... done
Downloading from network...
Download complete!
  File: restored.bin
  Size: 439.5 KB
  Time: 3.2s

# Private download (from local DataMap)
$ ant file download --datamap my_data.bin.datamap -o restored.bin
Connecting to network... done
Downloading from network...
Download complete!
  File: restored.bin
  Size: 439.5 KB
  Time: 2.8s
```

**Options:**

| Flag | Description |
|------|-------------|
| `ADDRESS` | Hex-encoded public DataMap address (64 hex chars). |
| `--datamap <PATH>` | Path to a local `.datamap` file (for private downloads). |
| `-o, --output <PATH>` | Output file path (default: `downloaded_file`). |

### `ant chunk` — Single-Chunk Operations

Low-level put/get for individual chunks (max ~1 MB each). Useful for small data or building custom data structures.

#### `ant chunk put [FILE]`

Store a single chunk. Reads from `FILE` or stdin. Requires `SECRET_KEY`.

```
$ echo "small payload" | SECRET_KEY=0x... ant chunk put
a1b2c3d4...
```

#### `ant chunk get <ADDRESS>`

Retrieve a single chunk by its hex-encoded XOR address.

```
$ ant chunk get a1b2c3d4... -o output.bin
```

**Options:**

| Flag | Description |
|------|-------------|
| `-o, --output <PATH>` | Write to file instead of stdout. |

### `ant wallet` — Wallet Operations

Inspect the EVM wallet derived from `SECRET_KEY`.

#### `ant wallet address`

Print the wallet's EVM address.

```
$ SECRET_KEY=0x... ant wallet address
0x1234567890abcdef...
```

#### `ant wallet balance`

Query the token balance on the configured EVM network.

```
$ SECRET_KEY=0x... ant wallet balance --evm-network arbitrum-one
1000000000000000000
```

### `ant node` — Node Management

Manage Autonomi network nodes via a local daemon process. The daemon runs in the background, exposes a REST API on `127.0.0.1`, and supervises all node processes.

#### `ant node daemon start`

Launch the daemon as a detached background process. By default it binds to a random free port on `127.0.0.1` and writes the chosen port to `daemon.port` for discovery.

```
$ ant node daemon start
Daemon started (pid: 12345, port: 48532)
```

**Options:**

| Flag | Description |
|------|-------------|
| `--port <PORT>` | Pin the HTTP port. `0` means OS-assigned (the default behavior). |
| `--listen-addr <IP>` | Bind address. Defaults to `127.0.0.1`. |

Pin the port and bind on all interfaces — useful when the daemon runs inside a container and the API needs to be reachable through a port mapping:

```
$ ant node daemon start --listen-addr 0.0.0.0 --port 8765
```

```
$ docker run -d -p 8765:8765 my/ant-image \
    ant node daemon start --listen-addr 0.0.0.0 --port 8765
```

> **Warning:** the daemon has no authentication. Binding to a non-loopback address exposes node management — start, stop, reset, registry mutation — to anyone who can reach the port. Only do this when the network path is controlled (e.g. a container with an explicit port mapping or a trusted private network).

#### `ant node daemon stop`

Shut down the running daemon. Sends SIGTERM and waits for exit.

```
$ ant node daemon stop
Daemon stopped (pid: 12345)
```

#### `ant node daemon status`

Show daemon status and node count summary.

```
$ ant node daemon status
Daemon is running
  PID:           12345
  Port:          48532
  Uptime:        3600s
  Nodes total:   3
  Nodes running: 2
  Nodes stopped: 1
  Nodes errored: 0
```

#### `ant node daemon info`

Output connection details as JSON (always JSON, regardless of `--json` flag). AI agents use this to discover the daemon's REST API.

```json
{
  "running": true,
  "pid": 12345,
  "port": 48532,
  "api_base": "http://127.0.0.1:48532/api/v1"
}
```

#### `ant node add`

Register one or more nodes in the registry. Does **not** start them. Does **not** require the daemon.

```
$ ant node add --rewards-address 0xYourWallet --count 3 --node-port 12000-12002 --path /path/to/antnode
Added 3 node(s):
  Node 1: port 12000
  Node 2: port 12001
  Node 3: port 12002
```

If the daemon is running, the command routes through its REST API. Otherwise, it operates directly on the registry file.

**Options:**

| Flag | Description |
|------|-------------|
| `--rewards-address <ADDR>` | Required. EVM wallet address for node earnings. |
| `--count <N>` | Number of nodes to add (default: 1). |
| `--node-port <PORT\|RANGE>` | Port or range (e.g., `12000` or `12000-12004`). |
| `--data-dir-path <PATH>` | Custom data directory prefix. |
| `--log-dir-path <PATH>` | Custom log directory prefix. |
| `--path <PATH>` | Path to a local `antnode` binary. |
| `--version <X.Y.Z>` | Download a specific version (accepts pre-releases, e.g. `0.17.0-beta.1`). |
| `--url <URL>` | Download binary from a URL archive. |
| `--bootstrap <IP:PORT>` | Bootstrap peer(s), comma-separated. |
| `--evm-network <NET>` | EVM network for storage payments: `arbitrum-one` (default) or `arbitrum-sepolia`. |
| `--upgrade-channel <CHAN>` | Release channel the node tracks for automatic upgrades: `stable` (default) or `beta`. Also selects which release the binary is downloaded from when no `--path`/`--version`/`--url` is given. |
| `--env <K=V>` | Environment variables, comma-separated. |

With none of `--path`, `--version` or `--url`, the binary is downloaded from the newest release
eligible for the chosen channel: the latest stable release on `stable`, and the highest of the
`-beta.N` and stable releases on `beta`. Release candidates (`-rc.N`) are never installed on
either channel — they are cut before the release gates have given a verdict.

#### `ant node start`

Start registered node(s). Requires the daemon to be running.

```
$ ant node start                          # Start all
$ ant node start --service-name node1     # Start specific node
```

#### `ant node stop`

Stop running node(s). Requires the daemon.

```
$ ant node stop                           # Stop all
$ ant node stop --service-name node1      # Stop specific node
```

#### `ant node status`

Display all nodes and their current status.

#### `ant node reset`

Remove all node data, log directories, and clear the registry. All nodes must be stopped first.

```
$ ant node reset --force
```

#### `ant node logs forward`

Optionally ship this machine's node logs to the Autonomi beta log endpoint, to help judge a beta
build. Off by default and never enabled for you — running `enable` yourself is the whole of the
consent, and `disable` stops it. See
[Forwarding your node logs](#forwarding-your-node-logs-optional) for the walkthrough.

```
$ ant node logs forward enable --token <token>
$ ant node logs forward status
$ ant node logs forward disable
```

**`enable` options:**

| Flag | Description |
|------|-------------|
| `--token <TOKEN>` | The write-only token issued with your beta enrolment. Only needed the first time; re-enabling after a `disable` reuses the stored one. |
| `--endpoint <URL>` | Send somewhere other than the default endpoint. Intended for testing against a local sink. |
| `--level <LEVEL>` | Lowest level to forward: `trace`, `debug`, `info`, `warn`, `error`. Defaults to `info`. |

`status` reports whether forwarding is on, which nodes are being tailed, which are being skipped and
why, and how many events have been delivered:

```
$ ant node logs forward status
Log forwarding: on
  Endpoint: https://logs.autonomi.com   Level: INFO and above
  Token:    75f57160d2c2

  Forwarding (1)
    ● node1 (1)

  Not forwarding (2)
    ○ node2 (2) — logging is not enabled for this node — re-add it with --log-dir-path to forward its logs
    ○ node3 (3) — logging is not enabled for this node — re-add it with --log-dir-path to forward its logs

  Node logging is off unless a node was added with --log-dir-path.

  Delivery
    Forwarded: 1284   Batches: 7 sent, 0 failed
```

The token is stored at `~/.config/ant/log_forward.json` with owner-only permissions and is never
printed back or returned by the API — `status` shows a short fingerprint of it instead, enough to
tell which token is in use.

### `ant update` — Self-Update

Replace the running `ant` binary with the newest release from GitHub. The downloaded archive's
ML-DSA-65 signature is verified against the embedded release signing key before anything is
installed, and the extracted binary must report the expected version.

```
$ ant update
Current version: 0.3.3
Checking for updates on the stable channel...
Update available: v0.3.3 -> v0.3.4
Updated successfully: v0.3.3 -> v0.3.4
```

**Options:**

| Flag | Description |
|------|-------------|
| `--channel <CHAN>` | Release channel to update along: `stable` or `beta`. Defaults to the channel the running binary belongs to. |
| `--force` | Re-download and reinstall even when already on the newest release. |

The default channel is inferred from the running binary's own version, so there is nothing to
configure: a `-beta.N` build tracks `beta` and every other build tracks `stable`. On `beta`,
both `-beta.N` and stable releases are accepted and the highest wins; `-rc.N` releases are
rejected on both channels.

Because a beta version outranks the stable release it was cut from, `--channel stable` cannot
walk a beta build backwards — it will report that you are already up to date. Leaving the beta
channel means installing a stable build manually.

---

## Beta Programme

The beta channel carries the week's build ahead of the stable train, for people who want to soak it
and report back.

Taking part means [running beta builds](#running-beta-builds). That is the whole requirement.

Separately, and entirely optionally, you can [forward your node logs](#forwarding-your-node-logs-optional)
to us. It is off by default, it is not a condition of being on the beta channel, and choosing not to
turn it on costs you nothing.

### Running beta builds

The client and the nodes have separate beta channels, opted into separately.

```bash
# 1. Install the beta client. Pass ANT_CHANNEL=beta to the quick-start installer:
$ curl -fsSL https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.sh \
      | ANT_CHANNEL=beta bash

#    On Windows:
#      $env:ANT_CHANNEL="beta"; irm https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.ps1 | iex

# 2. From then on, self-update stays on beta with no flag needed.
$ ant update

# 3. Add nodes that track the beta channel. The binary is downloaded from the newest
#    beta-eligible ant-node release.
$ ant node add --rewards-address 0xYourWallet --count 3 --upgrade-channel beta
```

Existing nodes are not switched to the beta channel by any of this — `--upgrade-channel` applies
to nodes at the point they are added, so opting in means adding new nodes.

### Forwarding your node logs (optional)

**This is opt-in and stays off until you ask for it.** Nothing leaves your machine unless you run
`ant node logs forward enable` yourself, and running it is the whole of the consent. You can skip
this section entirely and still run beta builds.

If you do want to help: beta enrolment comes with a write-only token, and handing it to that command
makes the daemon tail your nodes' log files and ship them to the Autonomi beta log endpoint. It is
the difference between us judging a build on a handful of self-reported problems and judging it on
what the nodes actually did, so it is genuinely useful — but it is your call, and `disable` stops it
at any point.

**If you do turn it on, the order of these three steps matters:**

```bash
# 1. Add nodes WITH LOGGING ENABLED. Node file logging is off by default; without
#    --log-dir-path a node writes no log files at all and there is nothing to forward.
$ ant node add --rewards-address 0xYourWallet --count 3 \
      --upgrade-channel beta --log-dir-path ~/.local/share/ant

# 2. Enable forwarding BEFORE starting the nodes.
$ ant node logs forward enable --token <your-token>

# 3. Now start them.
$ ant node start
```

#### Why the order matters

**`--log-dir-path` has to be set when the node is added.** The value is a prefix — each node gets
`<prefix>/node-<id>/logs` — and there is no way to turn logging on for an existing node short of
removing it and adding it again. If you forget, `enable` will tell you —
those nodes appear under "Not forwarding" in `ant node logs forward status` — but the node has to be
re-added to fix it.

**Enable before you start, not after.** Enabling forwarding is forward-looking consent: for any log
file that *already exists*, the daemon starts reading from the end of it, so nothing written before
you opted in is ever uploaded. A node that has not run yet has no log file, so the one it creates
when it starts is read from its first line.

The practical difference is the node's startup line, which is where its version, commit and peer ID
are recorded, along with the bootstrap and listen-address lines that show whether it actually joined
the network. Enable first and those are captured. Enable afterwards and they are skipped, so every
event forwarded from that run is missing the fields that identify which build produced it — which is
most of what makes the logs useful.

If you have already started a node, restarting it does not necessarily help: the node appends to the
same daily file, so a restart on the same day resumes from where the daemon had got to rather than
from the new run's first line.

#### What gets sent

Worth knowing before you decide. Only events at `INFO` and above, from nodes with logging enabled.
Every event is tagged with the node ID, service name, binary version, release channel, and the OS
and architecture of the machine.

Each event also carries a random identifier generated when you first enable forwarding, stored in
`~/.config/ant/log_forward.json`. Because every participant writes into the same daily index, it is
there to stop two machines' events colliding and overwriting one another. It is generated from
random bytes — not from your hostname, MAC address or username — so it distinguishes your
installation from others without describing it.

Your machine's hostname is **not** sent. Your wallet and rewards address are not part of what the
daemon adds. Beyond those tags, the content is whatever `ant-node` itself wrote to its log at `INFO`
or above — the same lines you can read yourself in the node's log directory.

#### If something looks wrong

`ant node logs forward status` is the place to look. `Batches: N sent, M failed` with an error line
underneath means the endpoint is unreachable or rejecting the token. Delivery is best-effort by
design: it is bounded in memory, it retries a few times and then gives up on a batch, and it never
blocks or slows a node. Losing some log lines is an acceptable outcome; a stalled node is not.

`disable` takes effect immediately: it waits for any request already in flight to be abandoned
before reporting that forwarding has stopped, so nothing is still being uploaded once the command
returns.

Forwarding survives a daemon restart, picking up where it left off without re-sending what it had
already delivered. If you want a genuinely clean slate, `disable` first, then delete
`~/.local/share/ant/log_forward_offsets.json`.

### Turning forwarding off

You can stop forwarding at any time, without leaving the beta channel and without giving a reason:

```bash
$ ant node logs forward disable
```

Nothing else about your nodes changes — no restart, no change to how they run, no data touched. They
keep writing their own logs to disk exactly as before; the daemon just stops reading them.

### Leaving the beta channel

Separate from the above, and manual. Because a beta version outranks the stable release it was cut
from, `ant update --channel stable` cannot walk a beta build backwards — installing a stable build
means downloading it yourself. Nodes already on `--upgrade-channel beta` stay on it.

---

## REST API

When the daemon is running, it exposes a REST API on `127.0.0.1:<port>`. Discover the port via `ant node daemon info` or by reading `~/.local/share/ant/daemon.port`.

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/status` | Daemon health, uptime, node count summary |
| GET | `/api/v1/events` | SSE stream of real-time node events |
| GET | `/api/v1/nodes/status` | Node status summary |
| POST | `/api/v1/nodes` | Add nodes to the registry |
| DELETE | `/api/v1/nodes/{id}` | Remove a node |
| POST | `/api/v1/nodes/{id}/start` | Start a specific node |
| POST | `/api/v1/nodes/start-all` | Start all registered nodes |
| POST | `/api/v1/nodes/{id}/stop` | Stop a specific node |
| POST | `/api/v1/nodes/stop-all` | Stop all running nodes |
| POST | `/api/v1/reset` | Reset all node state (fails if nodes running) |
| GET | `/api/v1/logs/forward` | Beta log-forwarding status, tailed nodes, delivery counters |
| POST | `/api/v1/logs/forward/enable` | Enable beta log forwarding |
| POST | `/api/v1/logs/forward/disable` | Disable beta log forwarding |
| GET | `/api/v1/openapi.json` | OpenAPI 3.1 specification |
| GET | `/console` | Web status console (HTML) |

### Error Envelope

All error responses use a consistent envelope:

```json
{
  "error": {
    "code": "NODE_NOT_FOUND",
    "message": "No node with id 42"
  }
}
```

### Idempotency

409 Conflict responses include `current_state` so retrying clients can confirm the desired state already exists:

```json
{
  "error": {
    "code": "NODE_ALREADY_RUNNING",
    "message": "Node 3 is already running"
  },
  "current_state": {
    "node_id": 3,
    "status": "running",
    "pid": 12345,
    "uptime_secs": 3600
  }
}
```

### SSE Events

`GET /api/v1/events` streams real-time node lifecycle events:

```
event: node_started
data: {"node_id": 1, "pid": 12345}

event: node_crashed
data: {"node_id": 2, "exit_code": 1}
```

Event types: `node_starting`, `node_started`, `node_stopping`, `node_stopped`, `node_crashed`, `node_restarting`, `node_errored`, `download_started`, `download_progress`, `download_complete`.

---

## Rust Library API (`ant-core`)

The `ant-core` crate exposes the full API programmatically. Add it as a dependency:

```toml
[dependencies]
ant-core = { path = "ant-core" }
```

### Connecting to the Network

```rust
use ant_core::data::{Client, ClientConfig};

// Connect to bootstrap peers
let client = Client::connect(&["1.2.3.4:12000".parse()?], ClientConfig::default()).await?;

// Attach a wallet for paid operations (uploads)
use ant_core::data::{Wallet, EvmNetwork};
let wallet = Wallet::new_from_private_key(EvmNetwork::ArbitrumOne, "0xprivate_key...")?;
let client = client.with_wallet(wallet);
client.approve_token_spend().await?;
```

### Uploading and Downloading Files

```rust
use std::path::Path;
use ant_core::data::PaymentMode;

// Upload a file (streamed, never fully loaded into memory)
let result = client.file_upload(Path::new("photo.jpg")).await?;
println!("Stored {} chunks", result.chunks_stored);

// Upload with explicit payment mode
let result = client.file_upload_with_mode(Path::new("photo.jpg"), PaymentMode::Merkle).await?;

// Store DataMap publicly (anyone with the address can download)
let public_address = client.data_map_store(&result.data_map).await?;

// Download a public file
let data_map = client.data_map_fetch(&public_address).await?;
client.file_download(&data_map, Path::new("photo_copy.jpg")).await?;
```

### Uploading and Downloading In-Memory Data

```rust
use bytes::Bytes;

// Upload bytes (encrypted + chunked automatically)
let result = client.data_upload(Bytes::from("hello autonomi")).await?;

// Download and decrypt
let content = client.data_download(&result.data_map).await?;
assert_eq!(content, Bytes::from("hello autonomi"));
```

### Low-Level Chunk Operations

```rust
use ant_core::data::XorName;

// Store a single chunk (< MAX_CHUNK_SIZE bytes)
let address: XorName = client.chunk_put(Bytes::from("small data")).await?;

// Retrieve it
if let Some(chunk) = client.chunk_get(&address).await? {
    println!("Got {} bytes", chunk.content.len());
}

// Check existence without downloading
let exists: bool = client.chunk_exists(&address).await?;
```

### Payment Modes

Autonomi supports two payment strategies:

| Mode | Description |
|------|-------------|
| `PaymentMode::Single` | One EVM transaction per chunk. Simple but more gas for many chunks. |
| `PaymentMode::Merkle` | Single EVM transaction for a batch of chunks via merkle proof. Lower gas for large uploads. |
| `PaymentMode::Auto` | Default. Uses merkle when chunk count >= 64, otherwise per-chunk. |

### Local Development with Devnet

Two ready-made examples spin up a local network and write a manifest to the shared data directory (`~/.local/share/ant/` on Linux, `~/Library/Application Support/ant/` on macOS, `%APPDATA%\ant\` on Windows). Any consumer that checks this directory — ant-gui, ant-cli, ant-tui — will auto-detect the devnet.

```bash
# Local Anvil devnet (25 nodes + embedded EVM blockchain)
# Includes a pre-funded wallet key in the manifest
cargo run --release --example start-local-devnet

# Sepolia testnet devnet (25 nodes + real Arbitrum Sepolia contracts)
# No wallet key — connect your own funded Sepolia wallet
cargo run --release --example start-devnet-sepolia
```

Both write `devnet-manifest.json` to the shared data dir and clean it up on Ctrl+C.

The [ant-gui](https://github.com/WithAutonomi/ant-ui) desktop app auto-detects the manifest on startup and switches to devnet/Sepolia mode — no manual configuration needed. The manifest can also be passed to the CLI explicitly via `--devnet-manifest <path>`.

#### Programmatic usage

```rust
use ant_core::data::LocalDevnet;

// Start a devnet (25 nodes + Anvil EVM chain)
let devnet = LocalDevnet::start(DevnetConfig::default()).await?;

// Create a client with a pre-funded wallet (ready to upload)
let client = devnet.create_funded_client().await?;

// Write manifest for discovery by other tools
let manifest_path = ant_core::config::data_dir()?.join("devnet-manifest.json");
devnet.write_manifest(&manifest_path).await?;

// Clean up
devnet.shutdown().await?;
```

### Chunk Cache

The client includes an in-memory LRU cache for recently accessed chunks:

```rust
let cache = client.chunk_cache();
cache.put(address, content);
if let Some(data) = cache.get(&address) { /* ... */ }
```

### Node Management (Programmatic)

```rust
use ant_core::node::{add_nodes, AddNodeOpts, BinarySource};
use ant_core::node::binary::NoopProgress;
use ant_core::config::data_dir;

let opts = AddNodeOpts {
    count: 3,
    rewards_address: "0xYourWallet".to_string(),
    binary_source: BinarySource::LocalPath("/path/to/antnode".into()),
    ..Default::default()
};

let result = add_nodes(opts, &data_dir()?.join("node_registry.json"), &NoopProgress).await?;
```

---

## Architecture

```
┌──────────┐     HTTP      ┌──────────────────────────────────┐
│  ant CLI │──────────────▶│         ant daemon                │
└──────────┘  127.0.0.1    │                                  │
                           │  ┌────────────┐ ┌────────────┐  │
┌──────────┐     HTTP      │  │  antnode 1  │ │  antnode 2  │  │
│  Web UI  │──────────────▶│  └────────────┘ └────────────┘  │
└──────────┘               │  ┌────────────┐ ┌────────────┐  │
                           │  │  antnode 3  │ │  antnode N  │  │
┌──────────┐     HTTP      │  └────────────┘ └────────────┘  │
│ AI Agent │──────────────▶│                                  │
└──────────┘               └──────────────────────────────────┘
                                       │
                                       ▼
                              node_registry.json
```

The daemon manages node processes, exposing a REST API on localhost. No admin privileges required. The CLI, web UI, and AI agents all communicate over HTTP.

Data operations (upload/download) go directly to the P2P network — they do not require the daemon. The daemon is only needed for node management.

## Project Structure

```
├── ant-core/                    # Headless library — all business logic
│   ├── src/
│   │   ├── lib.rs
│   │   ├── config.rs            # Platform-appropriate data/log paths
│   │   ├── error.rs             # Unified error type
│   │   ├── data/                # Data storage and retrieval
│   │   │   ├── mod.rs           # Re-exports and module declarations
│   │   │   ├── error.rs         # Data operation errors
│   │   │   ├── network.rs       # P2P network wrapper
│   │   │   └── client/          # High-level client API
│   │   │       ├── mod.rs       # Client, ClientConfig
│   │   │       ├── chunk.rs     # chunk_put, chunk_get, chunk_exists
│   │   │       ├── data.rs      # data_upload, data_download, data_map_store/fetch
│   │   │       ├── file.rs      # file_upload, file_download (streaming)
│   │   │       ├── payment.rs   # pay_for_storage, approve_token_spend
│   │   │       ├── quote.rs     # get_store_quotes from peers
│   │   │       ├── merkle.rs    # Merkle batch payment (PaymentMode)
│   │   │       └── cache.rs     # In-memory LRU chunk cache
│   │   └── node/                # Node management
│   │       ├── mod.rs           # add_nodes, remove_node, reset
│   │       ├── types.rs         # DaemonConfig, NodeConfig, AddNodeOpts, etc.
│   │       ├── events.rs        # NodeEvent enum, EventListener trait
│   │       ├── binary.rs        # Binary resolution, ProgressReporter trait
│   │       ├── registry.rs      # Node registry (CRUD, JSON, file locking)
│   │       ├── devnet.rs        # LocalDevnet (local network + Anvil EVM)
│   │       ├── daemon/
│   │       │   ├── mod.rs
│   │       │   ├── client.rs    # Daemon client (start/stop/status via HTTP)
│   │       │   ├── server.rs    # HTTP server (axum), REST API handlers
│   │       │   └── supervisor.rs # Process supervision with backoff
│   │       └── process/
│   │           ├── mod.rs
│   │           ├── spawn.rs     # Spawning node processes
│   │           └── detach.rs    # Platform-specific session detachment
│   └── tests/                   # Integration tests
├── ant-cli/                     # CLI binary (thin adapter layer)
│   └── src/
│       ├── main.rs              # Entry point, client/wallet initialization
│       ├── cli.rs               # clap argument definitions
│       └── commands/
│           ├── data/
│           │   ├── file.rs      # ant file upload/download
│           │   ├── chunk.rs     # ant chunk put/get
│           │   └── wallet.rs    # ant wallet address/balance
│           └── node/
│               ├── add.rs       # ant node add
│               ├── daemon.rs    # ant node daemon start/stop/status/info/run
│               ├── logs.rs      # ant node logs forward enable/disable/status
│               ├── start.rs     # ant node start
│               ├── stop.rs      # ant node stop
│               ├── status.rs    # ant node status
│               └── reset.rs     # ant node reset
└── docs/                        # Architecture documentation
```

## EVM Networks

| Value | Network | Use case |
|-------|---------|----------|
| `arbitrum-one` | Arbitrum mainnet | Production |
| `arbitrum-sepolia` | Arbitrum Sepolia testnet | Staging / testing |
| `local` | Custom (Anvil) | Local development (requires `--devnet-manifest`) |

## Environment Variables

| Variable | Required for | Description |
|----------|-------------|-------------|
| `SECRET_KEY` | Uploads, wallet commands | EVM private key (hex, with or without `0x` prefix) |

## Development

```bash
# Build
cargo build

# Run all tests
cargo test --all

# Lint
cargo clippy --all-targets --all-features -- -D warnings

# Format check
cargo fmt --all -- --check

# Run the CLI
cargo run --bin ant -- --help
cargo run --bin ant -- file upload photo.jpg --public --devnet-manifest ~/.local/share/ant/devnet-manifest.json --allow-loopback --evm-network local
cargo run --bin ant -- node daemon status
```

## License

Licensed under either of

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT]LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.