Demo images / Build & push demo images (push) Failing after 2m13s
The repo is source code and guidelines only. Nothing about how Archipelago's own fleet is run, or how the team works, stays in it. Untracked (kept on disk, gitignored) — 250 files: - .planning/ (199) and loop/ — internal development process - fleet operations tooling that targets specific nodes: deploy-to-target, deploy-tailscale, deploy-config-defaults, setup-target-dev, setup-aiui-server, setup-https-dev, debug-frontend, node-profile, fleet-fips-pair/unpair, image-recipe/sync-from-live.sh - image-recipe/INTEGRATION-GUIDE.md and docs/multinode-testing-plan.md, both of which are live-server workflow and fleet node inventories - the Phase 10 on-node verification and evidence records, which cite .planning/ as their evidence base KEY-05-ENTROPY-ENFORCEMENT.md was initially moved out with the other Phase 10 docs and then put back: it is cited as normative rationale from ten places in the codebase, including core/clippy.toml, which bans rand::thread_rng and points at it for the reason. That makes it a guideline, not an internal record. Node names removed from source (48 occurrences across comments, manifests and test fixtures): archi-dev-box, archy-x250*, shorty-s, framework-pt, zaza-optiplex, archi-thinkpad. Comments keep the engineering context and the date, which is what carried the meaning; the machine name did not. Three of those were live test values rather than comments and were replaced with valid stand-ins, not prose: two mDNS hostnames and a mesh peer name. An earlier pass substituted "a test node" into a hostname assertion, producing an invalid hostname; caught and fixed as test-node.local. Wipe mechanism: .local-only/manifest.txt inventories every local-only path and .local-only/wipe.sh deletes them on one confirmation, refusing to touch anything git still tracks. Both are themselves untracked, so the public repo does not carry a map of internal filenames. Verified: cargo check -p archipelago --all-features clean; archipelago-container 75/75 tests pass; appOrigin vitest 7/7; audit-secrets 5/5; every relative link in tracked markdown resolves (0 broken). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
89 lines
4.5 KiB
Desktop File
89 lines
4.5 KiB
Desktop File
[Unit]
|
|
Description=Archipelago Backend
|
|
After=network-online.target archipelago-setup-tor.service
|
|
Wants=network-online.target
|
|
# The data dir AND podman's graphroot (containers/storage) both live on the
|
|
# separate /var/lib/archipelago volume. Without this, on a cold boot the service
|
|
# (and its ExecStartPre) can start BEFORE var-lib-archipelago.mount, write to the
|
|
# bare mountpoint on rootfs, fail every podman call, exit, and get restarted every
|
|
# 5s until the volume mounts (~5 min of "[FAILED] Failed to start" on boot — B17).
|
|
# RequiresMountsFor adds both Requires= and After= on the mount unit so we never
|
|
# start until the data volume is mounted.
|
|
RequiresMountsFor=/var/lib/archipelago
|
|
|
|
[Service]
|
|
Type=notify
|
|
User=archipelago
|
|
Environment="ARCHIPELAGO_BIND=127.0.0.1:5678"
|
|
Environment="ARCHIPELAGO_USE_QUADLET_BACKENDS=true"
|
|
EnvironmentFile=-/var/lib/archipelago/telemetry.env
|
|
# DEV_MODE disabled in production — enabled via override.conf on dev servers
|
|
Environment="XDG_RUNTIME_DIR=/run/user/1000"
|
|
# + prefix runs these as root (needed for chown/mkdir outside ReadWritePaths)
|
|
ExecStartPre=+/bin/bash -c 'mkdir -p /run/user/1000 /var/lib/containers && chown archipelago:archipelago /run/user/1000 && chmod 700 /run/user/1000'
|
|
# Host IP from the main-table default route — hostname -I token order breaks
|
|
# once a VPN/bridge interface exists (netbird's wg tunnel sorted first and
|
|
# poisoned every host_ip consumer). Falls back to hostname -I when routeless.
|
|
ExecStartPre=+/bin/bash -c 'mkdir -p /var/lib/archipelago && chown archipelago:archipelago /var/lib/archipelago && IP=$(ip -4 route show default 2>/dev/null | sed -n "s/.* src \([0-9.]*\).*/\1/p" | head -1); [ -n "$$IP" ] || IP=$(hostname -I 2>/dev/null | awk "{print $$1}"); echo "ARCHIPELAGO_HOST_IP=$$IP" > /var/lib/archipelago/host-ip.env && chown archipelago:archipelago /var/lib/archipelago/host-ip.env'
|
|
# OTA crash-loop guard: if a just-applied binary can't start (SEGV loop), the
|
|
# in-binary post-OTA probe never runs — this restores the update-backup binary
|
|
# after 5 failed start attempts while the pending-verify marker exists.
|
|
# "-" so a missing/failed guard can never block the service itself.
|
|
ExecStartPre=+-/opt/archipelago/scripts/ota-crash-guard.sh
|
|
ExecStart=/usr/local/bin/archipelago
|
|
# always (not on-failure): the OTA restart path once stopped the daemon
|
|
# cleanly and the queued start never fired (a test node, v1.7.114->115,
|
|
# 2026-07-26) — the node sat dead all night behind "server starting up".
|
|
# Restart=always self-heals any lost start job; an explicit
|
|
# `systemctl stop` is still honored (systemd never auto-restarts after
|
|
# a manual stop).
|
|
Restart=always
|
|
RestartSec=5
|
|
WatchdogSec=300
|
|
TimeoutStartSec=300
|
|
# Backend shuts down in <1s; 15s is generous for any cleanup
|
|
TimeoutStopSec=15
|
|
|
|
# Filesystem protection
|
|
ProtectSystem=strict
|
|
# ProtectHome=no: rootless podman needs writable ~/.local/share/containers
|
|
ProtectHome=no
|
|
# PrivateTmp disabled: rootless podman runtime lives in /tmp/podman-run-UID/
|
|
# and must be shared between the service and SSH-created containers
|
|
ReadWritePaths=/var/lib/archipelago /etc/containers /var/lib/containers /run/user /tmp /home/archipelago/.local/share/containers /home/archipelago/.config/containers /etc
|
|
|
|
# Privilege restriction — NoNewPrivileges=no required for sudo archipelago-wg
|
|
# (WireGuard peer management). Scoped via sudoers to only archipelago-wg.
|
|
NoNewPrivileges=no
|
|
PrivateDevices=no
|
|
SupplementaryGroups=dialout debian-tor fips
|
|
|
|
# Syscall and network restrictions — safe on Debian 13 (systemd 256+)
|
|
# which respects NoNewPrivileges=no as an explicit override for seccomp filters
|
|
SystemCallArchitectures=native
|
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
|
RestrictRealtime=yes
|
|
|
|
# MemoryDenyWriteExecute removed: ring (rustls) and secp256k1 (bitcoin/nostr)
|
|
# use assembly code that requires executable memory mappings on some platforms
|
|
|
|
# Resource limits
|
|
MemoryMax=4G
|
|
LimitNOFILE=65535
|
|
TasksMax=2048
|
|
|
|
# Delegate cgroup controllers so rootless podman (run from this system service
|
|
# as user=archipelago, not user@1000.service) can create transient libpod-*.scope
|
|
# units with --memory / --cpus / --pids-limit. Without this, podman create fails
|
|
# at start time with: "MemoryMax is out of range" because systemd rejects resource
|
|
# limits on undelegated cgroup subtrees. Required for the ProdContainerOrchestrator
|
|
# code path (see core/archipelago/src/container/prod_orchestrator.rs).
|
|
Delegate=memory pids cpu io
|
|
|
|
# Logging
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|