Answer your agents from your phone.

aacpanel runs one Linux machine and the Claude Code sessions on it.

heliosssh · tmux attach
⎿ Read 88 lines (ctrl+o to expand)
● Bash(docker logs billing-api -n 40)
⎿ migrate: relation "invoices" is
locked by pid 3120
● The lock is held on purpose by
billing-api. I asked it to let go
after the dry run.
 
> run the orders migration tonight,
wake me if it fails
 
● Read(internal/store/orders.go)
⎿ Read 214 lines
● Bash(psql -c "EXPLAIN ALTER TABLE…")
⎿ 9 lines (ctrl+o to expand)
 
● The dry run shows three indexes to
rebuild on orders, about twelve
minutes with the site up. Which do
you prefer:
1. Now
2. After the backup at 02:00
3. Dry run only
> afetr the bacj^C
? for shortcuts ⏵⏵ auto mode
 
Interrupted · What should Claude do
instead?
[0] 0:claude* 1:zsh 2:claude- 3:billing"helios" 01:41 29-Sep-26
Over ssha typo and a stray Ctrl-C

36.4%·waiting for you·/srv/shopfront

billing-api is unhealthy since the 20:40 deploy — have a look

28 Sep · 20:58
thinking

The probe times out because the service never finishes starting: the migration step waits. A migration that never finishes usually means a lock.

The health check on billing-api fails after the new image: the log ends on a migration that never ran.

from session28 Sep · 21:02
billing-api

The lock on invoices is mine: I am holding a transaction open until the dry run answers. Say when to let go.

billing-api holds the lock on purpose, so the container is not the problem — the lock is. I rebuild the index on a copy first and measure.

thinking

Three indexes on orders go, one composite comes back. By the row count that is about twelve minutes with the site up.

Dry run first. Three indexes go, one comes back:

ALTER TABLE orders
  DROP INDEX orders_customer_idx,
  ADD INDEX orders_customer_placed_idx
      (customer_id, placed_at);

About twelve minutes with the site up — I would run it at 02:10, after the backup.

to subagent28 Sep · 21:08
index-review

Take the plan in docs/migration-plan.md and argue with it. I care about the rollback, not the wording.

from subagent28 Sep · 21:10
index-review

The rollback is wrong: dropping the new index leaves the old one gone. Recreate it in the same transaction, or the site reads without an index for twelve minutes.

Fair. The rollback now recreates orders_customer_idx in the same transaction.

files for you28 Sep · 21:12

The plan and the incident write-up, both short.

page28 Sep · 21:13
thinking

Nothing runs on the live table until the person says when.

The plan and the incident note are in docs/, the page has the order of the steps and the rollback. The dry run on the copy is still going.

!docker compose ps billing-api
NAME          IMAGE                          STATUS
billing-api   registry.example/billing:2.14  Up 34 minutes (unhealthy)
billing-db    postgres:16-alpine             Up 34 minutes (healthy)

run the orders migration tonight, wake me if it fails

28 Sep · 21:15

also run it on staging first

taken back — the session did not read it
1 command, 1 agent28 Sep · 21:31
from subagent28 Sep · 21:31
migration-dry-run

Dry run finished: 3 indexes to rebuild, 40 GB table, 11 min 40 s. No lock held longer than 2 s.

The dry run on the copy took 11 min 40 s and held no lock longer than 2 s. I look at the backup at 01:40 and ask you then when to run it.

command stopped28 Sep · 21:40
wake-up29 Sep · 01:40

check the backup, then ask when to run the orders migration

brief29 Sep · 01:42

request 3 min ago

In a handone tap
120%
waiting for you·36.4%·/srv/shopfront

billing-api is unhealthy since the 20:40 deploy — have a look

28 Sep · 20:58
thinking

The probe times out because the service never finishes starting: the migration step waits. A migration that never finishes usually means a lock.

The health check on billing-api fails after the new image: the log ends on a migration that never ran.

from session28 Sep · 21:02
billing-api

The lock on invoices is mine: I am holding a transaction open until the dry run answers. Say when to let go.

billing-api holds the lock on purpose, so the container is not the problem — the lock is. I rebuild the index on a copy first and measure.

thinking

Three indexes on orders go, one composite comes back. By the row count that is about twelve minutes with the site up.

Dry run first. Three indexes go, one comes back:

ALTER TABLE orders
  DROP INDEX orders_customer_idx,
  ADD INDEX orders_customer_placed_idx
      (customer_id, placed_at);

About twelve minutes with the site up — I would run it at 02:10, after the backup.

to subagent28 Sep · 21:08
index-review

Take the plan in docs/migration-plan.md and argue with it. I care about the rollback, not the wording.

from subagent28 Sep · 21:10
index-review

The rollback is wrong: dropping the new index leaves the old one gone. Recreate it in the same transaction, or the site reads without an index for twelve minutes.

Fair. The rollback now recreates orders_customer_idx in the same transaction.

files for you28 Sep · 21:12

The plan and the incident write-up, both short.

page28 Sep · 21:13
thinking

Nothing runs on the live table until the person says when.

The plan and the incident note are in docs/, the page has the order of the steps and the rollback. The dry run on the copy is still going.

!docker compose ps billing-api
NAME          IMAGE                          STATUS
billing-api   registry.example/billing:2.14  Up 34 minutes (unhealthy)
billing-db    postgres:16-alpine             Up 34 minutes (healthy)

run the orders migration tonight, wake me if it fails

28 Sep · 21:15

also run it on staging first

taken back — the session did not read it
1 command, 1 agent28 Sep · 21:31
from subagent28 Sep · 21:31
migration-dry-run

Dry run finished: 3 indexes to rebuild, 40 GB table, 11 min 40 s. No lock held longer than 2 s.

The dry run on the copy took 11 min 40 s and held no lock longer than 2 s. I look at the backup at 01:40 and ask you then when to run it.

command stopped28 Sep · 21:40
wake-up29 Sep · 01:40

check the backup, then ask when to run the orders migration

brief29 Sep · 01:42
  1. Read the log since the deploy28 Sep · 21:00
  2. Find what holds the lock28 Sep · 21:03
  3. Write the migration plan28 Sep · 21:13
  4. Dry-run the migration on a copy28 Sep · 21:31
  5. Run the migration after the backupsince 01:40
  6. Re-run the health check
  7. Write the incident note

request 3 min ago

At a deskthe same conversation, wider

Free. MIT, self-hosted, no account, nothing calls home.

Evening

Keep the machine up

After the deploy, billing-api went unhealthy. A restart says what it costs before it runs.

in a hand
cpu31%
memory48%
network90 KB/s
A restart names its cost
Stacks, and a live log
The host's load
Top processes
Probes
Alerts
at a desk
The log beside the list
Processes on the right
Alerts, “silent N”

Watch it work

shopfront reads the log, argues with a subagent, and writes a plan.

in a hand

28.9%·answering·/srv/shopfront
thinking

Nothing runs on the live table until the person says when.

The plan and the incident note are in docs/, the page has the order of the steps and the rollback. The dry run on the copy is still going.

!docker compose ps billing-api
NAME          IMAGE                          STATUS
billing-api   registry.example/billing:2.14  Up 34 minutes (unhealthy)
billing-db    postgres:16-alpine             Up 34 minutes (healthy)

run the orders migration tonight, wake me if it fails

28 Sep · 21:15
nowthinkingfor 1:16
The feed, as it happens
Who needs you
A subagent argues back
Letters and files
The calls behind a turn
Its subagents
Background work, an alarm
A subagent's own feed
at a desk
The work column
The Files tab
Night

Tell it what to do

“Run the migration tonight, wake me if it fails.” Typed, dictated or picked from a list.

in a hand

40.6%·idle·/srv/shopfront

billing-api is unhealthy since the 20:40 deploy — have a look

28 Sep · 20:58
thinking

The probe times out because the service never finishes starting: the migration step waits. A migration that never finishes usually means a lock.

The health check on billing-api fails after the new image: the log ends on a migration that never ran.

from session28 Sep · 21:02
billing-api

The lock on invoices is mine: I am holding a transaction open until the dry run answers. Say when to let go.

billing-api holds the lock on purpose, so the container is not the problem — the lock is. I rebuild the index on a copy first and measure.

thinking

Three indexes on orders go, one composite comes back. By the row count that is about twelve minutes with the site up.

Dry run first. Three indexes go, one comes back:

ALTER TABLE orders
  DROP INDEX orders_customer_idx,
  ADD INDEX orders_customer_placed_idx
      (customer_id, placed_at);

About twelve minutes with the site up — I would run it at 02:10, after the backup.

to subagent28 Sep · 21:08
index-review

Take the plan in docs/migration-plan.md and argue with it. I care about the rollback, not the wording.

from subagent28 Sep · 21:10
index-review

The rollback is wrong: dropping the new index leaves the old one gone. Recreate it in the same transaction, or the site reads without an index for twelve minutes.

Fair. The rollback now recreates orders_customer_idx in the same transaction.

files for you28 Sep · 21:12

The plan and the incident write-up, both short.

page28 Sep · 21:13
thinking

Nothing runs on the live table until the person says when.

The plan and the incident note are in docs/, the page has the order of the steps and the rollback. The dry run on the copy is still going.

!docker compose ps billing-api
NAME          IMAGE                          STATUS
billing-api   registry.example/billing:2.14  Up 34 minutes (unhealthy)
billing-db    postgres:16-alpine             Up 34 minutes (healthy)

run the orders migration tonight, wake me if it fails

28 Sep · 21:15

also run it on staging first

taken back — the session did not read it
1 command, 1 agent28 Sep · 21:31
from subagent28 Sep · 21:31
migration-dry-run

Dry run finished: 3 indexes to rebuild, 40 GB table, 11 min 40 s. No lock held longer than 2 s.

The dry run on the copy took 11 min 40 s and held no lock longer than 2 s. I look at the backup at 01:40 and ask you then when to run it.

command stopped28 Sep · 21:40
wake-up29 Sep · 01:40

check the backup, then ask when to run the orders migration

brief29 Sep · 01:42
question29 Sep · 01:46
MigrationThe orders migration is ready. Run it now, or after the backup at 02:00?After the backup
permission29 Sep · 01:48
Bashdocker compose -f deploy/compose.yml exec db pg_dump -Fc -t orders -f /backup/orders-0043.dumpAllowed, not asked again
from session29 Sep · 01:52
billing-api

Released the lock on invoices: the container restarted at 01:51 and its health check is green.

All set: the table is saved, the lock is gone, and the migration starts at 02:10, after the backup. I wake you only if it fails.

request 6 min ago

Slash commands
Pick the model
The permission mode
Photos and files
Dictation
/btw, on the side
at a desk
Commands, described

It asks. You answer.

Now, or after the backup? One tap, from bed.

in a hand

36.4%·waiting for you·/srv/shopfront

billing-api is unhealthy since the 20:40 deploy — have a look

28 Sep · 20:58
thinking

The probe times out because the service never finishes starting: the migration step waits. A migration that never finishes usually means a lock.

The health check on billing-api fails after the new image: the log ends on a migration that never ran.

from session28 Sep · 21:02
billing-api

The lock on invoices is mine: I am holding a transaction open until the dry run answers. Say when to let go.

billing-api holds the lock on purpose, so the container is not the problem — the lock is. I rebuild the index on a copy first and measure.

thinking

Three indexes on orders go, one composite comes back. By the row count that is about twelve minutes with the site up.

Dry run first. Three indexes go, one comes back:

ALTER TABLE orders
  DROP INDEX orders_customer_idx,
  ADD INDEX orders_customer_placed_idx
      (customer_id, placed_at);

About twelve minutes with the site up — I would run it at 02:10, after the backup.

to subagent28 Sep · 21:08
index-review

Take the plan in docs/migration-plan.md and argue with it. I care about the rollback, not the wording.

from subagent28 Sep · 21:10
index-review

The rollback is wrong: dropping the new index leaves the old one gone. Recreate it in the same transaction, or the site reads without an index for twelve minutes.

Fair. The rollback now recreates orders_customer_idx in the same transaction.

files for you28 Sep · 21:12

The plan and the incident write-up, both short.

page28 Sep · 21:13
thinking

Nothing runs on the live table until the person says when.

The plan and the incident note are in docs/, the page has the order of the steps and the rollback. The dry run on the copy is still going.

!docker compose ps billing-api
NAME          IMAGE                          STATUS
billing-api   registry.example/billing:2.14  Up 34 minutes (unhealthy)
billing-db    postgres:16-alpine             Up 34 minutes (healthy)

run the orders migration tonight, wake me if it fails

28 Sep · 21:15

also run it on staging first

taken back — the session did not read it
1 command, 1 agent28 Sep · 21:31
from subagent28 Sep · 21:31
migration-dry-run

Dry run finished: 3 indexes to rebuild, 40 GB table, 11 min 40 s. No lock held longer than 2 s.

The dry run on the copy took 11 min 40 s and held no lock longer than 2 s. I look at the backup at 01:40 and ask you then when to run it.

command stopped28 Sep · 21:40
wake-up29 Sep · 01:40

check the backup, then ask when to run the orders migration

brief29 Sep · 01:42

request 3 min ago

One tap: after the backup
Permission, from the page
Answered, in the feed
A brief for later
Tuesday, 29 September01:41
aacpanelnow
Question · shopfront

Migration: The orders migration is ready. Run it now, or after the backup at 02:00?

aacpanel5 h ago
Container unhealthy · billing-api

healthcheck failing · billing · Up 4 days (unhealthy)

The push, on a locked phone
Which pushes
at a desk
The question, as a window
Permission under the talk
A brief, full width

Take the wheel, or hand it back

Its own terminal, where it lives, and the way to close it.

in a hand

36.4%·waiting for you·/srv/shopfront
⎿ 01:38 orders: 31.2 GB of 40 GB
 
● aacpanel - brief_publish (MCP)
⎿ Published as orders-migration-window
 
──────────────────────────────────────
☐ Migration
 
The orders migration is ready. Run it
now, or after the backup at 02:00?
 
❯ 1. Now
twelve minutes, the site stays up
2. After the backup
the session wakes itself at 02:10
3. Dry run only
explain plan, no writes
4. Type something.
 
Enter to select · ↑/↓ to navigate · Esc
 
 
[shopfront0:claude*
"helios" 01:48 29-Sep-26
Its own tmux
Where it lives
MCP servers
Close, then kill
at a desk
The session popover
The Terminal tab

Sessions use the panel too

It keeps a checklist, writes to billing-api, and calls you when it is done.

in a hand

40.6%·idle·/srv/shopfront

billing-api is unhealthy since the 20:40 deploy — have a look

28 Sep · 20:58
thinking

The probe times out because the service never finishes starting: the migration step waits. A migration that never finishes usually means a lock.

The health check on billing-api fails after the new image: the log ends on a migration that never ran.

from session28 Sep · 21:02
billing-api

The lock on invoices is mine: I am holding a transaction open until the dry run answers. Say when to let go.

billing-api holds the lock on purpose, so the container is not the problem — the lock is. I rebuild the index on a copy first and measure.

thinking

Three indexes on orders go, one composite comes back. By the row count that is about twelve minutes with the site up.

Dry run first. Three indexes go, one comes back:

ALTER TABLE orders
  DROP INDEX orders_customer_idx,
  ADD INDEX orders_customer_placed_idx
      (customer_id, placed_at);

About twelve minutes with the site up — I would run it at 02:10, after the backup.

to subagent28 Sep · 21:08
index-review

Take the plan in docs/migration-plan.md and argue with it. I care about the rollback, not the wording.

from subagent28 Sep · 21:10
index-review

The rollback is wrong: dropping the new index leaves the old one gone. Recreate it in the same transaction, or the site reads without an index for twelve minutes.

Fair. The rollback now recreates orders_customer_idx in the same transaction.

files for you28 Sep · 21:12

The plan and the incident write-up, both short.

page28 Sep · 21:13
thinking

Nothing runs on the live table until the person says when.

The plan and the incident note are in docs/, the page has the order of the steps and the rollback. The dry run on the copy is still going.

!docker compose ps billing-api
NAME          IMAGE                          STATUS
billing-api   registry.example/billing:2.14  Up 34 minutes (unhealthy)
billing-db    postgres:16-alpine             Up 34 minutes (healthy)

run the orders migration tonight, wake me if it fails

28 Sep · 21:15

also run it on staging first

taken back — the session did not read it
1 command, 1 agent28 Sep · 21:31
from subagent28 Sep · 21:31
migration-dry-run

Dry run finished: 3 indexes to rebuild, 40 GB table, 11 min 40 s. No lock held longer than 2 s.

The dry run on the copy took 11 min 40 s and held no lock longer than 2 s. I look at the backup at 01:40 and ask you then when to run it.

command stopped28 Sep · 21:40
wake-up29 Sep · 01:40

check the backup, then ask when to run the orders migration

brief29 Sep · 01:42
question29 Sep · 01:46
MigrationThe orders migration is ready. Run it now, or after the backup at 02:00?After the backup
permission29 Sep · 01:48
Bashdocker compose -f deploy/compose.yml exec db pg_dump -Fc -t orders -f /backup/orders-0043.dumpAllowed, not asked again
from session29 Sep · 01:52
billing-api

Released the lock on invoices: the container restarted at 01:51 and its health check is green.

All set: the table is saved, the lock is gone, and the migration starts at 02:10, after the backup. I wake you only if it fails.

request 2 min ago

Panel tools in a turn
A letter from billing-api
The checklist it keeps
Tuesday, 29 September02:23
aacpanelnow
shopfront is calling

The migration is done: 11 min 40 s, the site stayed up. The health check is green.

aacpanel42 min ago
Question · shopfront

Migration: The orders migration is ready. Run it now, or after the backup at 02:00?

“Wake me”, as a push
Morning

Read what it wrote

Every changed line leads back to the conversation that wrote it.

in a hand
What the branch changed
A diff in two layers
Who wrote this line
A page it made
The archive, resume
at a desk
120%

orders-migration

against main4 files
@@ -1,3 +1,6 @@in a commit
-- Orders: the customer index and the date index.
CREATE INDEX orders_customer_idx ON orders (customer_id);
CREATE INDEX orders_placed_idx ON orders (placed_at);
-- Orders: one index for the customer and the date instead of two.
ALTER TABLE orders
37f3355Dana Reyes11 h 1 m ago

One index for the customer and the date instead of two

DROP INDEX orders_customer_idx,
DROP INDEX orders_placed_idx,
ADD INDEX orders_customer_placed_idx
(customer_id, placed_at);
@@ -4,3 +4,9 @@ ALTER TABLE ordersnot in a commit yet
DROP INDEX orders_placed_idx,
ADD INDEX orders_customer_placed_idx
(customer_id, placed_at);
-- Rollback: the old index comes back in the same transaction,
-- so the site never reads orders without one.
-- ALTER TABLE orders
-- DROP INDEX orders_customer_placed_idx,
-- ADD INDEX orders_customer_idx (customer_id);
Code, diff, who wrote it
The archive panel

Know what it costs

What the night cost, by project, model and hour, and what is left before the limit.

in a hand
Where the tokens went
/context, broken down
at a desk
120%
homehelios · overview
Usage todaycollected 5 min ago

8.9minbound

5k out · 28 answers

Cache efficiencytoday

98%of the inbound came from cache

read 8.8m · written 135k

Usage by hourinboundoutput, right axis
Top projects30 days
Models and subagents30 days · share of inbound
  • opus-5-569%
  • sonnet-527%
  • haiku-4-54.3%
  • main sessions100%
  • subagents0.3%

2 subagents ran · 1.2% of the output

Tools30 days · 0.2% errors overall
Bash0.3% errors365 calls
Read9 calls
Edit4 calls
AskUserQuestion3 calls
Write3 calls
Home at a desk
Usage, crumb by crumb
Limit rings by contour
Setting up

Decide how sessions start

Accounts, groups and projects, and what each one starts with.

in a hand
The map
A project's page
A contour's defaults
The next launch, previewed
at a desk
120%

No session picked

The conversation opens here

The map in three columns
Launch settings

Only you get in

31named actions, and no “run this command”
  • A passkey, enrolled with a one-time code printed on the machine.
  • The part facing the internet only asks; a process on the host decides.
  • Your conversations never enter that container.
  • Every action is written down before it runs.
in a hand

aacpanel

the host at hand

The code is issued by a device that already has access — in the “devices” section. If there is none, aacpanel -enroll prints it on the host.

Passkey sign-in
Ways in
Devices
The journal
heliosthe panel is down
# the sessions run on without it
 
$ aacpanel-exec -sessions
 
$ aacpanel-exec -console shopfront
 
$ tmux attach -t shopfront
 
 
ssh helios
When the panel is down
at a desk
The journal panel
Three processes

About half an hour on a fresh machine

INSTALL.md goes through it in order. The one piece that acts on the machine you build yourself.

docker compose up -d
go build -o ~/bin/ ./cmd/aacpanel-exec
systemctl --user start aacpanel-exec
  • Linux + systemd
  • docker
  • Go
  • tmux
  • python3
  • claude, signed in