Skip to content

# Arcane

Web-based Docker management IDE. Main instance on CT 109 (`192.168.1.8:10002`, `arcane.nuclide.systems`). Manages containers on all Docker hosts via edge agents. Migrated from CT 104 → CT 109 on 2026-05-23.

Stack: `/opt/stacks/arcane/docker-compose.yml` on CT 109. 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 | Environment | Compose path | Status
--- | --- | --- | --- | ---
CT 101 | shepard | shepard | `/opt/stacks/ops-agents/docker-compose.yml` | online
CT 104 | docker | docker | `/opt/stacks/ops-agents/docker-compose.yml` | online
CT 105 | nextcloud | nextcloud | `/opt/stacks/ops-agents/docker-compose.yml` | online
CT 110 | id | id | `/opt/stacks/ops-agents/docker-compose.yml` | online
CT 111 | dev | dev | `/opt/stacks/ops-agents/docker-compose.yml` | online
CT 112 | secrets | secrets | `/opt/stacks/ops-agents/docker-compose.yml` | online
CT 113 | db | db | `/opt/stacks/db/docker-compose.yml` | online
nuc | nuc | NUC (built-in) | (built-in environment) | online

### 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):

    # 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":"<ctname>","apiUrl":"edge://<ctname>","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:

    # On PVE host, run: python3 /tmp/arcane-bootstrap.py
    import argon2, secrets, sqlite3, uuid
    from datetime import datetime, timezone

    db_path = "/rpool/data/subvol-109-disk-0/opt/stacks/arcane/data/arcane.db"
    env_id  = "<id from step 1>"          # 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

      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.8:10002
        deploy:
          resources:
            limits:
              cpus: "0.5"
              memory: 256M

Add `ARCANE_AGENT_TOKEN=<raw_token>` to the CT's `.env`.

Step 4 — Start and verify

    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=<uuid>

The environment should flip to `online` in `environments.status` within ~5 seconds.

## Migration to CT 109 — completed 2026-05-23

Arcane migrated from CT 104 → CT 109. All edge agents updated with `MANAGER_API_URL: http://192.168.1.8:10002`. Zoraxy upstream updated to `192.168.1.8:10002`. SQLite DB at `/opt/stacks/arcane/data/` on CT 109.

## 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:

    sqlite3 /rpool/data/subvol-109-disk-0/opt/stacks/arcane/data/arcane.db \
      "DELETE FROM api_keys WHERE name='admin-cli';"