File size: 6,129 Bytes
b2d9e47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# Deploy agentmemory on fly.io

This template runs agentmemory on a single fly.io machine with a 1 GB
persistent volume mounted at `/data`. The HMAC secret is generated on
first boot and persisted to the volume — you capture it from the deploy
logs exactly once.

## What you get

- A public HTTPS endpoint serving the agentmemory REST API on port 3111
- A 1 GB Fly Volume at `/data` for memories, BM25 index, and stream backlog
- `auto_stop_machines = "stop"` and `min_machines_running = 0` — the
  machine sleeps when idle, so cost floor approaches $0 for low traffic
- HTTP healthcheck at `/agentmemory/livez` every 30 s
- The HMAC bearer secret is generated on first boot inside the
  container and persisted to `/data/.hmac` (chmod 600); the operator
  copies it from the deploy logs once.

## One-time setup

Pick a unique Fly app name first — `agentmemory` itself is likely taken.
Every command below references `$APP`, so set it once and the rest of the
flow stays consistent:

```bash
# 1. Install flyctl: https://fly.io/docs/flyctl/install/
# 2. Pick your unique app name (and matching volume name):
export APP="agentmemory-$(whoami)"     # or any other globally-unique name
export VOLUME="${APP//-/_}_data"       # Fly volume names can't contain '-'

# 3. From this directory:
fly launch --copy-config --no-deploy --name "$APP"

# 4. Create the volume in the same region as the app:
fly volumes create "$VOLUME" --region iad --size 1

# 5. Deploy:
fly deploy --app "$APP"
```

If `fly launch` reports the name is taken, pick another value for `$APP`,
re-export, and re-run.

## Capture the HMAC secret

Right after the first deploy succeeds:

```bash
fly logs --app "$APP" | grep -A1 AGENTMEMORY_SECRET=
```

You will see exactly one line of the form `AGENTMEMORY_SECRET=<64 hex chars>`.
Copy it into your client environment (`~/.bashrc`, Claude Desktop config,
the viewer unlock prompt, etc.). The secret is never printed again on
subsequent boots.

If the first-boot log line is no longer available, read the persisted
secret from the mounted volume:

```bash
fly ssh console --app "$APP" -C "sh -lc 'cat /data/.hmac'"
```

## Verify the deployment

```bash
curl "https://$APP.fly.dev/agentmemory/livez"
# {"status":"ok"}
```

For an authenticated call, your client must send `Authorization: Bearer <secret>`.

## Viewer access (port 3113 stays internal)

The viewer port is intentionally not exposed publicly. Tunnel to it:

```bash
fly proxy 3113:3113 --app "$APP"
# then open http://localhost:3113
```

`fly proxy` opens an mTLS WireGuard channel to the machine, so the
viewer's bearer token still has to ride a loopback connection on your
laptop — the v0.9.12 plaintext-bearer guard stays satisfied.

The entrypoint sets `AGENTMEMORY_VIEWER_HOST=::` **only when it detects
Fly's runtime variables** (`FLY_APP_NAME` / `FLY_ALLOC_ID`). That makes
the viewer listen on the machine's `fly-local-6pn` WireGuard interface
as well as loopback so `fly proxy` can reach it. The same branch
pre-seeds `VIEWER_ALLOWED_HOSTS=localhost:3113,127.0.0.1:3113,[::1]:3113`,
which are the Host headers `fly proxy 3113:3113` actually emits on
your laptop.

When `AGENTMEMORY_VIEWER_HOST` is non-loopback the viewer enforces two
extra guards: it refuses to start unless `VIEWER_ALLOWED_HOSTS` is
explicitly set, and every request to `/agentmemory/*` must present
`Authorization: Bearer $AGENTMEMORY_SECRET`. Static HTML and the
favicon are still served unauthenticated. If a proxied viewer request
gets a 401, the browser UI prompts for `AGENTMEMORY_SECRET` and stores
it in session storage so subsequent viewer API calls include the bearer.
Use the value printed in the first-boot logs or read `/data/.hmac`
inside the machine.

> **Security warning.** Setting `AGENTMEMORY_VIEWER_HOST=0.0.0.0` or
> `::` turns the viewer into a network-reachable proxy that signs every
> upstream call with `AGENTMEMORY_SECRET`. Never enable that outside a
> network you trust (Fly's WireGuard mesh in this template), and never
> set it in a plain `docker run -p 3113:3113 …` on a shared host — the
> entrypoint deliberately skips the override when Fly env vars are
> absent so a plain Docker pull stays loopback-only.

## Rotate the HMAC secret

```bash
fly ssh console --app "$APP"
rm /data/.hmac
exit
fly machine restart <machine-id>
fly logs --app "$APP" | grep AGENTMEMORY_SECRET=
```

Update every client with the new secret. Old tokens stop working
immediately.

## Back up `/data`

```bash
fly ssh console --app "$APP" -C "tar czf - /data" > "$APP-$(date +%Y%m%d).tar.gz"
```

To restore on a fresh machine:

```bash
cat "$APP-YYYYMMDD.tar.gz" | fly ssh console --app "$APP" -C "tar xzf - -C /"
fly machine restart <machine-id>
```

## Cost floor and egress

- Idle (machine stopped): the volume costs ~$0.15/GB/month. A 1 GB
  volume is roughly $0.15/month.
- Active (machine running on `shared-cpu-1x` with 512 MB): about
  $1.94/month if it ran 24/7; in practice `auto_stop_machines` keeps
  that well under $1.
- Outbound bandwidth: 100 GB/month free on the Hobby plan, then $0.02/GB
  in North America / Europe.

See <https://fly.io/docs/about/pricing/> for the up-to-date rate card.

## Known caveats

- The volume lives in one region. To survive a region outage, create a
  second volume in another region and update `primary_region` after the
  failover, or take snapshots with `fly volumes snapshots create`.
- The Dockerfile builds in the Fly Builder on every deploy — first
  build is ~30 seconds; cached layers shrink rebuilds to under 10
  seconds. Image is ~114 MB.
- First deploy lands on a **shared IPv4 + dedicated IPv6** by default
  (free). If you need a dedicated IPv4 for legacy clients without SNI,
  run `fly ips allocate-v4 --app "$APP"` — costs $2/month.
- Cold-start (from machine launch to passing `/agentmemory/livez`) is
  ~9 seconds measured. `grace_period = "30s"` on the health check
  gives a 3x safety margin.
- Bump `AGENTMEMORY_VERSION` or `III_VERSION` in the Dockerfile to
  upgrade. `fly deploy --build-arg AGENTMEMORY_VERSION=<x>` also works
  for a one-off without editing the file.