Asterisk and Mattermost each used to embed their own dedicated coturn
container (network_mode: host), and their default relay port ranges
overlapped by ~100 UDP ports — running both on one box meant a
coin-flip over which service's active call lost its media relay.
- services/coturn.sh: new shared TURN/STUN relay, one instance for
every consumer instead of one each. Runs --lt-cred-mech with a
SQLite user database (not --use-auth-secret — coturn doesn't
support both auth mechanisms on one instance at once, confirmed via
coturn's own upstream docs/issues) so each consumer gets its own
dedicated username/password without stepping on any other's.
- lib/common.sh: ensure_coturn_user() — chain-installs coturn.sh on
first need (same declare -F guard pattern as the existing
asterisk -> security-dashboard chaining) and registers/reuses a
per-consumer credential, mirroring configure_caddy_for_service's
out-param convention.
- services/asterisk.sh: _asterisk_write_compose gains a
USE_EMBEDDED_COTURN flag. New installs use the shared service;
existing installs keep their dedicated coturn exactly as-is on
every "update" (detected from the existing compose file before
regenerating it, so a rebuild can never silently drop the container
its own .env TURN_PASSWORD still points at) and only switch on an
explicit "fresh" reinstall, with a warning first.
- services/mattermost.sh: same embedded/shared coturn handling, plus
genuine multi-instance support (separate dir/containers/DB/ports per
instance, auto-scanned free ports for extras) for real isolation
between groups, as opposed to Team Edition's built-in Teams feature.
Calls plugin TURN config switched from the HMAC "TURN Static Auth
Secret" field to the verified "ICE Servers Configurations" JSON
field, which accepts the same fixed username/credential shared
coturn issues. Also fixes a latent bug found while adding proper
update-mode detection: DB_PASS/MM_SECRET were regenerated on every
single rerun with no existing-install check at all, silently
breaking Postgres auth on any reinstall.
- CLAUDE.md: documents the ensure_coturn_user pattern (including the
auth-mechanism constraint and the embedded-coturn migration-safety
rule) for any future service that needs TURN.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NQkdAn3iG5A4WoqU9FHMaN
restore_kopia.sh is interactive and one-service-at-a-time, which doesn't
scale to standing up a cold spare box quickly during a real outage.
dr_bringup.sh restores every service's latest snapshot (or one named
service) and runs docker compose up -d with no prompts, so a full-stack
recovery is one command instead of N interactive restores.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NQkdAn3iG5A4WoqU9FHMaN
Matches the existing vendor/easy-asterisk convention (used by
services/asterisk.sh) instead of two one-off top-level directories that
cluttered the repo root and didn't look like anything else next to
setup.sh, lib/, services/, extras/. Only the two services' own SRC_DIR
path resolution and header comments needed updating — nothing else in
the repo referenced the old ./ai-stack / ./paintplus paths.
Also documents vendor/ in README.md's Layout section.
Applies the new write_readme companion-doc convention to ai-stack,
paintplus, and kyber-launcher: install-time-invariant content (usage
walkthroughs, service tables, troubleshooting) moves out of the .sh
heredocs into sibling services/<name>.md files, leaving only genuinely
install-specific content inline.
- services/paintplus.md: config/cloud/GPU/ai-stack-backend/update/Caddy
sections, picked up automatically via write_readme's companion-doc
support.
- services/ai-stack.md: roles, GPU switcher, service URLs, cloud LLM
provider setup, update, Caddy. ai-stack.sh can't use write_readme
directly (its POST-INSTALL-NOTES.md filename deliberately avoids
colliding with the vendored app's own README.md in the same
directory), so it appends the companion file manually.
- services/kyber-launcher.md: the full SWBF2 (2017) + Kyber walkthrough,
moved out of the root README's "Gaming scripts" section (which now
just points here). kyber-launcher.sh now calls write_readme to deploy
it to ~/.local/share/kyber/README.md, fixing a stale in-script pointer
to a README section that no longer exists.
New service for one narrow job — getting SMS verification codes sent to a
VoIP number onto a phone with no SIM. Deliberately not a texting app: no
outbound path (Anveo Direct has none; that needs an Anveo Retail account, and
a free texting app covers sending), and messages arrive as push notifications
rather than being routed into Asterisk as SIP MESSAGE, since a code you read
and type is better served by a notification than a softphone chat thread.
Two modes, both driven entirely from the provider's "forward SMS to URL" box:
- direct — the provider calls ntfy itself; nothing installed here. ntfy
accepts GET publishing at /{topic}/(publish|send|trigger) with message and
title as query params, and auth via ?auth= holding base64url (unpadded) of
the literal "Bearer <token>" — confirmed against ntfy's server.go and
server_auth.go rather than its docs.
- relay — a stdlib systemd service, Caddy-fronted on its own domain with no
Authelia (the provider can't log in; a random 32-char token in the path is
the secret). Buys two things direct mode can't have: an unescaped "&" in a
message body survives intact, because the relay takes everything after the
last message= verbatim instead of parse_qs — which is why the generated URL
always puts the message placeholder last — and no ntfy credentials sit in a
third party's web portal.
Verification codes are bearer credentials, so: a 24-char random topic name
(the repo's ntfy defaults to auth-default-access: read-write, making the topic
name the read credential), constant-time token compare, a 60/min rate limit,
and the relay logs sender/recipient/length but never the message body.
The Anveo guide gains a section covering the two things that actually decide
whether codes arrive: short-code support (Anveo has it, unusually — VoIP.ms
does not except for Google) and Anveo's carrier-sourced *mobile* DIDs, which
are classified as mobile in the lookups that reject VoIP numbers at signup.
Also documents MMS and group texts being out of reach, and why the native
Messages app never sees any of this.
Verified against a stub ntfy: plain OTP, encoded "&", unencoded "&", "+" as
space, wrong token (404), missing message (400) and the rate limit (57x204
then 429) all behave; both installer modes were run end to end in a sandbox
and their generated URLs, settings files and READMEs checked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAddJGE1G6eGaPzmScG5Vh
services/asterisk-digital-ocean.sh was a near-verbatim copy of
services/asterisk.sh — same vendor refresh, compose template, messaging
dialplan, presence alerts, UFW rules and dashboard/trunk chaining, with the
helper functions renamed _asterisk_do_*. Two copies meant every fix had to
land twice, and several never did.
There is now one `asterisk` service. It reads the DigitalOcean metadata
service and asks either way (so a droplet with metadata blocked, or another
provider's public VM, can still opt in), then gates the genuinely
droplet-specific behaviour on that one answer: swapfile for low-RAM plans,
public-FQDN-only setup with no LAN/VLAN prompts, a Caddy site block pinned
to that FQDN, the remote-Authelia option, and the doctl Cloud Firewall.
Two things that were droplet-only for no real reason now apply everywhere:
the entrypoint patch that writes security-level events to logs/full, and
the logrotate config for that file. Without them the Security Dashboard's
Security Log tab and CrowdSec's Asterisk acquisition were silently empty on
every home/LAN install; crowdsec.sh now detects either install directory.
Existing droplets are left alone: an install at ~/docker/asterisk-digital-ocean
keeps its directory and easy-asterisk-do container names, since its Caddyfile
block, UFW rules, Cloud Firewall, CrowdSec acquisition and PSTN trunk all
name those exactly. New installs use ~/docker/asterisk / easy-asterisk.
`sudo ./setup.sh asterisk-digital-ocean` still works via a new SERVICE_ALIAS
map in setup.sh, without a second menu entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAddJGE1G6eGaPzmScG5Vh
Generalizes services/pstn-trunk.sh (renamed from voipms-trunk.sh in the
prior commit) away from VoIP.ms specifics - any IP-authenticated SIP
provider works, VoIP.ms is just the suggested default. Adds:
- Role-based outbound permission: a configurable allow-list of extensions
that may dial PSTN numbers (regex-gated on CHANNEL(peername)), separate
from internal extension-to-extension dialing which stays open to everyone
regardless. Blank list preserves the original "everyone can dial out"
behavior.
- Inbound ring-group: rings a configurable list of extensions instead of a
single hardcoded one.
- ntfy alerts: immediate on denied (unauthorized extension) or rejected
(concurrency cap hit) calls, plus an hourly cron-driven check that alerts
once per month when estimated spend crosses a threshold and every hour
call volume looks like a burst. Uses a self-contained pipe-delimited call
log rather than Asterisk's CDR, to avoid depending on CDR module
availability and CSV comma-quoting.
- Settings persisted to .pstn-trunk.env so "update in place" reapplies
everything from that file instead of fragile re-parsing out of generated
Asterisk config (which had a real bug: update mode was extracting the
wrong Dial(PJSIP/...) line).
Tested end-to-end against a sandboxed copy of the real vendor files:
permission-gate regex, ring-group dial-string construction, ntfy line
injection/removal, and the usage-alert script's threshold/burst/monthly-
dedup logic all verified with synthetic data. Caught and fixed a sed `&`
escaping bug in the ring-group substitution before it shipped (RING_DIAL
contains literal `&` join characters, which sed's replacement syntax
otherwise treats as "insert the match").
Adds a VoIP.ms SIP trunk on top of asterisk-digital-ocean: IP-authenticated
trunk (no password stored), NANP-only outbound dialplan, a global 3-call
concurrent cap via GROUP()/GROUP_COUNT(), and inbound routing to one
extension. Config lives in its own include files rather than being
appended directly to pjsip.conf/extensions.conf, since Easy Asterisk fully
regenerates both from its own internal state — the includes are patched
into the vendor's generator functions so they survive that regeneration.
Wires the new service into setup.sh's is_installed() and README's services
table, and updates docs/pstn-calling-voipms-plan.md to reflect what's now
implemented vs. still open (spend/volume alerting, live-account
verification).
New service, native on the host (not Docker) so it can call cscli and
read Asterisk's security log directly without bridging the
container/host boundary or exposing CrowdSec LAPI credentials to a
containerized frontend.
- Security Log tab: parses ~/docker/asterisk-digital-ocean/logs/full
for SIP auth failures (wrong password, unknown extension, etc.) with
timestamp/account/remote IP, classified by severity.
- CrowdSec tab: current bans via cscli, a delete/unban button per
entry, and ASN-exempt management for the Asterisk brute-force
scenarios (services/crowdsec.sh) without SSHing in.
- Link out to the existing Asterisk web admin (reads its domain from
asterisk-digital-ocean's own .env, doesn't hardcode or embed it).
Runs as a dedicated unprivileged system user (secdash), with sudo
scoped to exactly three commands via /etc/sudoers.d/security-dashboard
(cscli decisions delete --id <digits>, cscli decisions list -o json,
systemctl restart crowdsec) — validated with visudo -c. Listens on
127.0.0.1 only, reachable through Caddy, and refuses to proceed without
explicit confirmation if no Authelia (local or remote) is configured,
since this page can delete active security bans.
Stdlib-only Python (no framework), matching the RAM-conscious pattern
already used for Easy Asterisk's own web admin. All embedded code
(bash, Python, JS) syntax-checked; the generated sudoers rule
validated with visudo -c -f.
Renamed services/asterisk-do.sh -> services/asterisk-digital-ocean.sh
(register_service name, install function, install dir, and all prose/
comments) so the whiptail menu shows a clearer, more discoverable name.
Updated the functional cross-references that depend on the old name:
crowdsec.sh's SIP-log auto-detection path and acquisition filename,
caddy.sh's host.docker.internal comment, and the CLAUDE.md/README.md
docs (services table, directory listing, network-wiring example).
Container names, the Docker Compose project name, and the internal
_asterisk_do_* helper function identifiers are left unchanged since
they aren't user-facing and renaming them would add risk for no
benefit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015X1jRGHwrvovz2qkhKfDZi
Duplicates services/asterisk.sh (left untouched) into a DO-specific
variant: auto-detects the droplet's public IP/ID via the DO metadata
service, always assumes a public FQDN (no LAN/VLAN prompts), offers to
provision a matching DigitalOcean Cloud Firewall via doctl (never
touching one that's already attached), and documents droplet sizing,
firewall rules, and Sipnetic client setup in the generated README.
Lets 'ssh <alias>' connect directly to user@host instead of retyping it —
especially useful once machines are reachable over NetBird/VPN and have
IPs that aren't worth memorizing.
- lib/common.sh: ssh_config_path/add_ssh_host_alias/list_ssh_host_aliases/
remove_ssh_host_alias helpers, operating on the invoking user's own
~/.ssh/config (not root's) with correct 700/600 permissions and ownership
- base.sh: after SSH key import, optionally add one or more Host aliases
interactively as part of the base install
- services/ssh-config.sh: new standalone service (sudo ./setup.sh ssh-config)
to list/add/remove aliases any time, independent of base install; follows
the existing non-Docker standalone-bootstrap pattern (see crowdsec.sh)
- setup.sh: ssh-config never shows [installed] since it's a repeatable
management tool, not a one-time install
- README: new 'SSH Host aliases' section, base row and wizard-flow step 1
updated, ssh-config added to the extras group and copiable service list
Verified end-to-end with a test harness: add with defaults, add with a
custom user/port, list (correct numbering), and remove-by-name preserving
the other entry and file permissions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQJBvqzXeyuhhAcAA3Q5Wq
The wizard description was stale — it still described the old
site-defaults-first flow and didn't mention that base now installs Docker,
openssh-server (with SSH key import), and NetBird, or that the wizard ends
by dropping into a fresh login shell so the docker group takes effect.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQJBvqzXeyuhhAcAA3Q5Wq
capture.sh already replaces motionEye/any NVR itself - it just needs
each camera's RTSP URL. services/sky-cam.sh never actually prompted for
CAM_RTSP_<cam>, so capture/audio never had anything to connect to.
- Prompt per camera for its RTSP URL -> CAM_RTSP_<cam> in .env
- Prompt for sunrise mic / optional ambient audio library
- Fix Mattermost integration: sunrise2mm.py reads mattermost_url/
access_token/channel_id (bot-token REST upload), not the
MM_WEBHOOK_URL/MM_CHANNEL incoming-webhook scheme the installer used
to write - uploads never worked before this
- Add optional ntfy push notifications
- Auto-generate SCHEDULE_SEASONS_<cam> (staggered 30 min apart) for
every configured camera, not just the stock east/north/south, so
install.sh wires up every applicable systemd timer for any camera set
Removes services/sky-cam-frigate.sh entirely - routing sky-cam's frames
through Frigate (via export API or restream) turned out to be solving a
problem that doesn't exist; sky-cam's own capture.sh talking directly to
each camera is simpler and has no quality/resolution tradeoffs. Frigate
continues to run fully independently for NVR/detection.
Duplicates services/sky-cam.sh into a Frigate-backed variant that pulls
recordings via Frigate's export API instead of a JPEG image folder.
Includes a frigate-retime.sh helper that exports a coarse timelapse,
measures its actual duration with ffprobe, and re-encodes once with a
computed setpts factor to hit an exact target length (e.g. a Four
Seasons movement's runtime).
Vendor the functional source of github.com/outis1one/local-ai into
./ai-stack (22 files) and add services/ai-stack.sh, which copies the
source to ~/docker/ai-stack and hands off to the app's VRAM-aware
installer (local-ai-setup.sh). The stack bundles Ollama, Open WebUI,
RAG + MCP servers, ChromaDB, SearXNG, Kiwix, Gitea, InvokeAI, ComfyUI
and Portainer.
Cloud LLM providers (Groq/DeepInfra/OpenAI/OpenRouter) are optionally
wired into Open WebUI via the plural OPENAI_API_BASE_URLS list, with the
local RAG connection kept as the first entry so RAG keeps working. Open
WebUI ships built-in auth, so Caddy is configured without Authelia.
Excludes the upstream's two bundled copies of this very project
(ubuntu-post-install.sh, ubuntu-post-install-main.zip) — stale and
circular. Coexists with the existing ai-gpu service.
Also fix the install-function names for ai-gpu and ai-stack: the
dispatcher calls install_<raw-name>, so the function must be
install_ai-gpu / install_ai-stack (hyphen), matching the working
mail-archiver / wg-easy services. ai-gpu was previously uninstallable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nb2vJ8W7bHKx1JXVvpCraH
Bring the full EditmaskwithAI application into the repo under paintplus/
(429 files) so the service is self-contained — the installer copies the
vendored source to ~/docker/paintplus/src instead of cloning at runtime.
Rename to PaintPlus (service + branding; app logic untouched):
- services/editmaskwithai.sh -> services/paintplus.sh (register_service
paintplus, install_paintplus, ~/docker/paintplus, Caddy paintplus:8000,
Authelia option preserved)
- container names -> paintplus across docker-compose*.yml; dev network
-> paintplus-network
- browser <title> -> "PaintPlus - AI Image Editor"; README heading ->
PaintPlus with upstream provenance note
- README utilities table: editmaskwithai -> paintplus
Backend/frontend code (help strings referencing the old container name,
the ai_photo_edit.db filename) is intentionally left as-is to avoid
touching application logic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nb2vJ8W7bHKx1JXVvpCraH
Package the self-hosted mask-based AI photo editor
(github.com/outis1one/EditmaskwithAI) as a utilities service. Clones the
repo into ~/docker/editmaskwithai and deploys on port 3080.
Two install modes:
- Cloud API (no GPU): writes .env from the repo template with the chosen
AI_PROVIDER (OpenAI gpt-image or Replicate), API key, and a generated
SECRET_KEY, then `docker compose up -d --build`.
- Local GPU (NVIDIA): runs the repo's own install-local-gpu.sh and
bring-up-local-gpu.sh (downloads ~13 GB of models).
The app has no built-in auth, so it offers Authelia SSO and reverse-proxies
via Caddy (ai-photo-edit:8000). Cloud mode joins caddy_net through a
docker-compose.override.yml; GPU mode is attached with `docker network
connect` since the GPU compose runs with an explicit -f and does not merge
overrides.
Also lists the service in the README utilities table.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nb2vJ8W7bHKx1JXVvpCraH
Installs the Kyber community multiplayer launcher (simonlinuxcraft/kyber-
linuxport-unofficial) via the setup.sh wizard. Handles glibc check, bwrap/
userns fix, GitHub release fetch, desktop entry, and bin symlink. Warns on
Intel integrated graphics and links to Sunshine as the streaming alternative.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
Installs Sunshine (LizardByte) as a Moonlight-compatible streaming host,
offset to port 48090 so it coexists with Wolf. Auto-detects the Kyber
AppImage and adds a "Kyber SWBF2" app entry. Optional virtual display for
closed-lid/headless use. uinput udev rules for controller/mouse input.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
- Auto-detects SWBF2 install path; loud error with install instructions if missing
- Downloads Kyber AppImage if not present, extracts kyber_cli, runs get_token
(opens browser for EA OAuth — one-time step, token never expires)
- Prompts for EA credentials (written to .env, chmod 600) and server name
- Auto-detects GPU (NVIDIA/Intel/AMD) and adds appropriate passthrough config
- Writes docker-compose.yml + .env to ~/docker/kyber-server/
- Map rotation left blank by default with instructions in .env comments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
- Add troubleshooting for 'Origin Error: title installed in language not
entitled to play' — Maxima's umu-run reg commands fail silently on some
systems; manual wine64 regedit import of Origin locale keys fixes it
- Add windowed-mode / HDR fix for SWBF2 crash during level load on
integrated GPUs (Intel Iris Xe, DXVK rendering crash)
- Document fix in both README.md and script header comments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
- Bots are set via BOTS TEAM 1 / BOTS TEAM 2 sliders in HOST panel (not automatic)
- Correct launch order: Steam open but don't click Play; Kyber launches SWBF2 itself
- Document Alt+Tab / taskbar click workaround for window focus issue after game loads
- Add bwrap unprivileged userns fix (Ubuntu 24.04 blocks by default); script applies
automatically with sudo and persists via /etc/sysctl.d/99-userns.conf
- Script now checks/fixes userns at step 0 before download
- Kyber manages its own GE-Proton; no need to configure Proton for SWBF2 in Steam
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
The Windows kyber_launcher.exe approach under Wine/Proton has two
unfixable problems on Linux: Wine's cmd.exe crashes with STATUS_ACCESS_VIOLATION
on the long EA OAuth URL, and EA's auth callback uses the eadesktop://
URI scheme which has no Linux handler.
The correct solution is the native Linux port AppImage maintained at
github.com/simonlinuxcraft/kyber-linuxport-unofficial. It bundles Maxima
(open-source EA Desktop replacement) and handles OAuth natively — no shims,
no watchers, no per-Proton-update maintenance.
Script now: fetches latest release from GitHub API, downloads AppImage,
installs desktop entry and ~/.local/bin/kyber symlink.
README: add Gaming scripts section documenting Kyber setup, private server
hosting with bots, requirements, and what does not work.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
iopaint: AI image inpainting (object removal, fill, restore) via IOPaint +
LaMa model. Runs CPU by default; GPU option writes nvidia deploy block.
No built-in auth — Authelia SSO prompt included. Port 8100.
ai-gpu: GPU AI stack from outis1one/ai-6gb-gpu. Clones repo and sets up
three stacks under ~/docker/ai-gpu/: InvokeAI image gen (port 9090),
Ollama + Open WebUI + SearXNG LLM stack (ports 11434/3000), and Flask
portal (port 8080) that hot-swaps the GPU between stacks. Patches
hardcoded home paths in portal docker-compose.yml to use ACTUAL_HOME.
Prompts for TZ (replaces hardcoded America/New_York). Caddy for both
portal (localai) and InvokeAI (images).
https://claude.ai/code/session_01JEu7LgCWXKhXo18MeYFRZp
- joplin: self-hosted sync server (PostgreSQL sidecar, APP_BASE_URL from SITE_DOMAIN)
- stirling-pdf: PDF toolkit with optional Authelia SSO (no built-in auth)
- n8n: workflow automation connecting self-hosted services (WEBHOOK_URL from SITE_DOMAIN)
- changedetection: web page change monitoring with playwright-chrome renderer
- archivebox: personal Wayback Machine, initializes data dir at install time
- calibre-web: ebook library UI with Calibre conversion support (linuxserver image)
- homebox: home inventory and asset management
All services follow the standalone bootstrap pattern and support local/remote Caddy.
README.md updated with new services in appropriate group rows.
Integrates https://github.com/outis1one/easy-asterisk into the post-install
system. Downloads the management script and coturn entrypoint at install time,
generates docker-compose.yml with host-networking Asterisk + coturn, writes
a randomised TURN password, and opens UFW ports for SIP/RTP/TURN.
Interactive FQDN setup chooses between LAN-only (UDP, no TLS) and FQDN mode
(TLS+SRTP+TURN) and prints required router port-forward instructions.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Apt-based service for Android/iPhone ↔ Linux integration: shared
clipboard, notifications, file transfer, remote input. Works on Ubuntu
(GNOME) and Linux Mint Cinnamon. Opens UFW ports 1714-1764 automatically.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Docker-based Syncthing with PUID/PGID ownership, caddy_net integration,
and standalone bootstrap support. Exposes web UI on 8384 and sync
protocol on 22000 (tcp+udp) and discovery on 21027/udp.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
New service: borg-backup (backup group)
- Installs borgbackup from apt
- Same coverage as Kopia backup: full ~/docker/<service>/ snapshots
- Minecraft: flush world (save-all), archive, no downtime
- All others: stop → archive → restart
- Multi-destination support with per-service routing
- SSH remote repos supported natively (user@host:/path)
- Per-service pruning: keep-daily/weekly/monthly
- repo compact after each run
- Systemd timer (with cron fallback)
- repokey-blake2 encryption; key export reminder in summary
- worker.sh: run / list / info subcommands
New restore helper: extras/restore_borg_backup.sh
- Installed to ~/docker/borg-backup/restore/<dest>/
- Interactive: lists services → pick archive → inspect (/tmp) or restore in-place
- In-place restore: stops container, moves current data aside, extracts, restarts
- Old data kept as .pre-restore-DATE with rollback instructions printed
https://claude.ai/code/session_019XgsQ13XKm4Zj3cNsDNwHj
- backup: renamed from disaster-backup; full ~/docker/<service>/ snapshots,
Minecraft flushed not stopped, everything else stop/snap/start for DB
consistency; backup.conf / restore/ layout; post-install-backup timer
- gaming-backup: renamed from backup; frequent game-save snapshots (Minecraft
world data, emulator saves, Steam, Wolf state) with no service downtime;
defaults to hourly schedule; separate gaming-kopia repo + gaming-backup timer
- Deleted services/disaster-backup.sh
- README: updated backup row to reflect new names
https://claude.ai/code/session_019XgsQ13XKm4Zj3cNsDNwHj
Minecraft instances are flushed to disk (save-all) and snapshotted while
the server keeps running — no player disruption. All other services stop
briefly for filesystem-consistent database snapshots, then restart.
Also update file header, install banner, schedule description, and README.
https://claude.ai/code/session_019XgsQ13XKm4Zj3cNsDNwHj
- minecraft: prompt for world seed on new installs (skipped if world already exists); SEED env var wired into docker-compose.yml
- disaster-backup: new service that stops each Docker service, snapshots the full ~/docker/<service>/ directory (config + data + databases), then restarts it — multi-destination support so different services can go to different drives/repos; one restore_kopia_backup.sh per destination
- README: update backup row to list both backup and disaster-backup
https://claude.ai/code/session_019XgsQ13XKm4Zj3cNsDNwHj
Documents all packages installed by the base service, and adds a
Backup section covering Kopia, Borg, plain rsync, rsync --link-dest
(versioned snapshots with original folder structure), and rsnapshot —
with guidance on when to use each.
https://claude.ai/code/session_019XgsQ13XKm4Zj3cNsDNwHj
When you open the USB folder in the file manager, you're already there.
Right-click → Open in Terminal → sudo bash bootstrap.sh, or double-click
bootstrap.sh → Run in Terminal → sudo prompt. No /media/whoami/DRIVENAME
path hunting required.
https://claude.ai/code/session_017WJtGcE5jjerAQCUBWUE3H