# Arcane Web-based Docker management IDE. Main instance on **CT 104** (`192.168.1.40:10002`, `arcane.nuclide.systems`). Manages containers on the local Docker host plus remote CTs via **edge agents**. Stack: `/opt/stacks/arcane/docker-compose.yml` on CT 104. Image: `ghcr.io/getarcaneapp/arcane:latest` Auth: OIDC via Pocket ID, admin: `fkrebs@nucli.de` --- ## Edge agents An edge agent (`ghcr.io/getarcaneapp/arcane-headless:latest`) runs inside each remote CT and connects **outbound** to the main Arcane server via gRPC poll. The main server then manages that CT's Docker. ### Currently deployed agents | CT | Hostname | Compose path | Status | |----|----------|--------------|--------| | CT 104 | docker | *(built-in NUC environment)* | online | | CT 113 | db | `/opt/stacks/db/docker-compose.yml` | online (2026-05-21) | ### Adding an edge agent to a new CT **Step 1 — Create the environment in Arcane** (requires Arcane API or UI access) With the admin CLI API key (stored in Arcane DB, regenerate if needed): ```bash # Get or create an admin API key — see "Admin API key" section below API_KEY="arc_..." curl -s -X POST -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \ "http://192.168.1.40:10002/api/environments" \ -d '{"name":"","apiUrl":"edge://","isEdge":true}' # Note the environment id from the response ``` **Step 2 — Generate and wire the AGENT_TOKEN** Arcane stores the raw AGENT_TOKEN in `environments.access_token`. Insert it via: ```python # On PVE host, run: python3 /tmp/arcane-bootstrap.py import argon2, secrets, sqlite3, uuid from datetime import datetime, timezone db_path = "/rpool/data/subvol-104-disk-0/opt/stacks/arcane/data/arcane.db" env_id = "" # paste the environment UUID here conn = sqlite3.connect(db_path) raw_token = "arc_" + secrets.token_hex(32) key_prefix = "arc_" + raw_token[4:12] ph = argon2.PasswordHasher(memory_cost=65536, time_cost=3, parallelism=2) key_hash = ph.hash(raw_token) key_id = str(uuid.uuid4()) now = datetime.now(timezone.utc).isoformat() conn.execute( "INSERT INTO api_keys (id, name, description, key_hash, key_prefix, environment_id, managed_by, created_at, updated_at)" " VALUES (?,?,?,?,?,?,?,?,?)", (key_id, f"Environment Bootstrap Key - {env_id[:8]}", "Auto-generated key for environment pairing", key_hash, key_prefix, env_id, "system", now, now)) conn.execute("UPDATE environments SET access_token=? WHERE id=?", (raw_token, env_id)) conn.commit() conn.close() print("AGENT_TOKEN:", raw_token) ``` **Step 3 — Add the agent to the CT's compose** ```yaml arcane-agent: image: ghcr.io/getarcaneapp/arcane-headless:latest container_name: arcane-agent restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock - ./arcane-agent:/app/data environment: EDGE_AGENT: "true" EDGE_TRANSPORT: poll AGENT_TOKEN: ${ARCANE_AGENT_TOKEN} MANAGER_API_URL: http://192.168.1.40:10002 deploy: resources: limits: cpus: "0.5" memory: 256M ``` Add `ARCANE_AGENT_TOKEN=` to the CT's `.env`. **Step 4 — Start and verify** ```bash docker compose up -d arcane-agent docker logs arcane-agent 2>&1 | grep "Edge gRPC tunnel" # Expected: Edge gRPC tunnel connected to manager environment_id= ``` The environment should flip to `online` in `environments.status` within ~5 seconds. --- ## When Arcane moves to CT 109 When Arcane migrates from CT 104 to CT 109 (the planned "observe" LXC): 1. Update `MANAGER_API_URL` in every edge agent's `.env` to the new CT 109 LAN IP 2. Update the Arcane compose `APP_URL` and network bindings 3. Update Zoraxy route for `arcane.nuclide.systems` to point to CT 109 4. Migrate the SQLite DB: `cp -a /opt/stacks/arcane/data/ /` 5. Regenerate the admin API key (stored in memory, not config) since the DB port changes 6. Restart all edge agents with the new `MANAGER_API_URL` Arcane environments (NUC, db, shepard) will reconnect automatically once `MANAGER_API_URL` is updated. --- ## Admin API key (non-OIDC access) Arcane doesn't store plaintext API keys — use the DB-insert method above when a new admin key is needed. The `python3-argon2` package must be installed on the PVE host (`apt install python3-argon2`). The key inserted for CLI use during CT 113 provisioning (`arc_a9695182...`) is linked to `fkrebs` user in the api_keys table. Rotate it after provisioning work is complete by deleting the row: ```sql sqlite3 /rpool/data/subvol-104-disk-0/opt/stacks/arcane/data/arcane.db \ "DELETE FROM api_keys WHERE name='admin-cli';" ```