Operations¶
Running the server day to day: the CLI, what you can configure, rotating a token, and what to check when something is wrong.
CLI¶
| Command | Purpose |
|---|---|
esky profile create <name> |
Create a profile database |
esky profile list |
List profiles |
esky token issue <profile> |
Issue a token, invalidating any previous one |
esky token status <profile> |
Whether a token has been issued, and when |
esky profile migrate <name> |
Bring a profile up to the current schema |
esky profile reindex <name> |
Recompute every embedding after an embedding change |
esky serve |
Run the server |
Run them via docker compose exec esky <command>.
Configuration¶
| Variable | Default | Notes |
|---|---|---|
ESKY_BIND |
127.0.0.1 |
Host interface the port publishes on |
ESKY_PORT |
8080 |
Published port |
ESKY_UID |
0 |
Host UID the containers run as; set to id -u |
ESKY_GID |
0 |
Host GID the containers run as; set to id -g |
ESKY_DATA_DIR |
/data |
Where profile databases live |
ESKY_EMBED_MODEL |
BAAI/bge-small-en-v1.5 |
384-dim; changing this needs a reindex |
ESKY_RRF_K |
60 |
Reciprocal rank fusion constant |
ESKY_MAX_DISTANCE |
0.9 |
Relevance floor for vector hits |
ESKY_HOST, ESKY_PORT and ESKY_DATA_DIR configure the app inside the
container, where the image already fixes them to 0.0.0.0, 8080 and
/data. Compose publishes the port and mounts the data directory, so the
.env entries for ESKY_PORT and ESKY_BIND describe the host side only.
The three tuning values below are the ones .env genuinely changes.
ESKY_MAX_DISTANCE is worth understanding: vector KNN returns its nearest
neighbours however unrelated they are, so without a floor a nonsense query
still hands the agent confident-looking facts. Measured L2 distances over
unit-normalised bge-small vectors on a seeded corpus put related queries at
0.54–0.74 and unrelated at 0.88–1.00.
Measure before you move it, because real traffic runs higher than that corpus
suggests. Agents send short keyword queries, which sit further from a stored
sentence than a seeded phrase does, so useful hits land between 0.62 and 0.88 —
most of them above the seeded "related" band. Raise the floor if nonsense
queries return facts; before lowering it, read /api/{profile}/queries and
check what distances your own good searches actually produce.
Rotating a token¶
Issuing a new token invalidates the old one immediately — there is no grace period — so every client using it stops working until you update it. Rotate the server first, then each client:
# on the server
docker compose exec esky esky token issue personal
# on every machine connected to that profile
claude mcp remove -s user esky
claude mcp add -s user --transport http esky \
http://192.168.1.10:8011/mcp/personal \
--header "Authorization: Bearer <new-token>"
Run token issue in a plain terminal, not inside an AI chat session — a token
pasted into a transcript should be treated as burned. Rotating costs seconds,
which makes "reissue whenever a token has been near a chat window" a cheap
default.
claude mcp list reports ✔ Connected or a 401, so it doubles as a check
that a rotation landed. Clients pick up the new config on their next
session, not the current one.
Rotate when a token has been pasted somewhere it shouldn't, when a machine that held it is decommissioned, or when you want to cut off one client without disturbing the others — for that last case, give it its own profile instead, since a profile's token is shared by everything pointed at it.
Troubleshooting¶
| Symptom | Cause |
|---|---|
curl hangs, returns nothing |
Malformed URL. curl reads 192.161 as the IP 192.0.0.161 and waits. Check the full address and the :8011 port — without it, curl tries port 80. Always pass -m 5. |
{"error":"unauthorized"} |
Missing, wrong, or another profile's token; or no token issued for that profile; or the profile does not exist. All return an identical 401 by design — see Security. |
./data/*.db owned by root, unreadable from the host |
Set ESKY_UID/ESKY_GID in .env, then fix the existing files once: docker run --rm -v "$PWD/data:/data" alpine chown -R $(id -u):$(id -g) /data |
Connection refused from another machine |
ESKY_BIND is still 127.0.0.1. Set it to the LAN IP and docker compose up -d. |
| Connection times out from another machine | Host firewall. Check sudo ufw status. |
/mcp shows the server but no tools |
Token rejected at connect. Test the same token with the /api/profiles curl in connecting. |
| Tool descriptions look out of date | Clients cache them at session start. Restart the Claude session. |
| Connected, but the agent never recalls anything | Expected without the instruction — see Tell your agent to use it. |
Server-side checks:
docker compose ps # running and healthy?
docker compose logs --tail=50 esky # errors?
docker compose exec esky esky profile list # which profiles exist
docker compose exec esky esky token status work # is a token issued
curl -s http://127.0.0.1:8011/health # needs no token
Next: security · connecting an agent