Persistent aria2c as a systemd user service (pos-aria2.service, enable --now, linger warning) on localhost:6800 with a generated RPC secret in ~/.config/linux_post_install/download.env (chmod 600, env override). 18 commands: start/stop/status, add/torrent/metalink (auto-start, --tmux live view), list/info/files/peers, pause/resume/remove/purge/move, limit/set, watch (2s live repoll). JSON built via jq -nc --arg, never string interpolation. Deps: aria2 in preinstall PACKAGES, aria2c/jq/curl guards before --help. Docs: POS.md + howto/network.md + indices + Common Tasks row.
25 KiB
AGENT_TODO — Worklist & Idea Backlog
Living list of what we are doing, what is next, and what we might do later.
Deep history lives in git: git log --follow AGENT_TODO.md, git blame, and
the individual feature commits — the Done section below is just a readable
summary (newest last).
Conventions
- Now — items actively being worked on this session (only a few).
- Next — queued, well-scoped items.
- Later — idea backlog. Ideas marked NOT NOW were evaluated and rejected for the stated reason; revisit only if circumstances change.
- When a task is completed: move it from Now/Next into Done (dated one-line) in the same commit that finishes the work.
Done
-
2026-08-11 — Docs: DEV.md / AGENTS.md / AGENT_Context improved from the SMB session's lessons. DEV.md: new "Testing tools that need root / systemd / missing deps" (env-override test seams —
FLAGS_DIR/SMB_CONF/SMB_CREDS_DIR/UNIT_DIRprecedents — + stub-PATH fakes + PTY prompt driving viascript); new Best Practice "Managed Config Blocks" (start/end marker idiom incl. theinblock == 1awk guard, validate-then-apply, hot reload); deps-guards-run-before---helpmade explicit (previously only inferable by reading the NFS tools); "Update the docs" checklist completed (howto index/section, Common Tasks row, AGENTS.md Quick facts, AGENT_TODO Done move). AGENTS.md: clarified which filetable line-count rows are hand-maintained (non-pos-*files above the marker) + when to bump them; deps-guard clause added to Quick facts. AGENT_Context "Adding a New Tool" steps 6–7 mirror the above.make gen && make checkgreen. -
2026-08-11 —
sharecategory grows SMB:pos share smb server(bin/pos-share-smb-server) +pos share smb client(bin/pos-share-smb-client), completing the share trio (usb/nfs/smb). Server:status/share/unshare/list/adduser/deluser/reload/enable/disable; idempotent marker blocks in/etc/samba/smb.conf(# >>> pos-managed share: <name>…# <<< end pos-managed share— hand edits outside markers survive;inblock==1-guarded awk so removing one block never eats another's end marker),testparmvalidation before apply +smbcontrol smbd reload-confighot reload;--read-only/--guest/--users u1,u2flags with unrestricted-share warnings;smbpasswduser management (prompts, requires system user first). Client:mount/unmount/list/persist/unpersist; password prompt via/dev/tty, throwaway chmod-600 credentials for one-shot mounts, persistent creds at/etc/samba/credentials/<name>(chmod 600);persistwrites a systemd.mountunit (systemd-escape) withx-systemd.automount+_netdev— mounts on first access, never blocks boot. Both sourcelib/notify.shfor mutations; added toINTERACTIVE_CMDS(prompting subcommands). Deps:samba+cifs-utilsadded to preinstall PACKAGES.SMB_CONF/SMB_CREDS_DIR/UNIT_DIRenv-overridable for tests (FLAGS_DIR precedent). Docs: POS.md share rows, howto/share.md SMB sections, HOWTO index row, AGENT_Context Common Tasks, AGENTS.md categories.make gen && make checkgreen; logic tested via stubbed PATH + temp config (marker idempotency, guest + user persist flows). -
2026-08-11 —
pos network checkportnmap overhaul: two-pass engine — pass 1 = fast-Pn -T4 --max-retries 1scan of only the asked ports (was: all 65535) with per-port state + nmap service names; pass 2 (--versions, opt-in) =-sV --version-lighton open ports only (generous host-timeout — version probing a silent service otherwise made nmap skip the host entirely), fallback fast banner probe for open TCP with no version info; TCP fast path ~2s for 3 ports. Unprivileged UDP now falls back to the nc engine (Debian nmap-sUrequires root and quit outright); IPv6 hosts get-6;no output/filtered states set rc=1;--timeoutscales nmap host-timeouts. New--versionsflag in# POS_FLAGS:(completions regenerated) + usage text; port-metadata fallback retained.make gen && make checkgreen. -
2026-08-11 —
pos communication matrix sender loginerror reporting: captures HTTP status + Matrixerrcode/errorfrom the JSON body (temp file, not stdout) instead of a generic "wrong credentials?" message — distinguishes unreachable homeserver from rejected credentials; auto-prepends@when--useris bare (e.g.--user alice:example.org→@alice:example.org). -
2026-08-11 — New
sharecategory —usbandnfsmoved out ofpos usb/pos systemintopos share:pos share usb server(waspos-usb-server),pos share nfs server+pos share nfs client(werepos-system-nfs-*). Renamed the three tools (bin/pos-share-*), updated# POS:headers/usage strings,INTERACTIVE_CMDS(usb-server→share-usb-server),bin/posusage() EXAMPLES, and the notify-scope comment inpos-system-backup. Docs: newDOC/howto/share.md(USB + NFS consolidated;howto/usb.mddeleted, NFS sections stripped fromhowto/system.md), POS.md### sharesection (replaces### usb, nfs rows moved out of### system), HOWTO/README indices, AGENT_Context hand-written spots, root README, DEV.mdINTERACTIVE_CMDSexample, AGENTS.md categories. Category is the home for futuresmb.make gen && make checkgreen;/usr/local/binrefreshed. -
2026-08-09 — Matrix/Synapse
communicationtools —pos communication matrix sender+listener, completing the second notify platformlib/notify.shwas designed for (NOTIFY_PLATFORM=telegram,matrixfan-out; the sender implements thesend <value> [--markdown]contract vianotify_sender_name()'s default key→tool mapping, no lib changes). Sender (bin/pos-communication-matrix-sender):send <value> [--markdown] [--room <id|alias>]PUTsm.room.message(m.text) to the client-server API v3 — room ids/aliases URL-encoded (#pos:example.org→%23pos%3A…), unique per-message txn id,--markdownsendsorg.matrix.custom.htmlvia a best-effort markdown→HTML converter (bold/italic/code/fences/strike/links/headers/lists, escapes HTML, never fails the send);login --user <@id>(masked password prompt →m.login.password→ savesaccess_token+user_id);test. Config scopematrix(~/.config/linux_post_install/matrix.env,MATRIX_HOMESERVER/MATRIX_ACCESS_TOKEN/MATRIX_USER_ID/MATRIX_ROOM_ID, secret masked) registered via# POS_CONFIG:→pos config matrix+ tab-completion scope. Listener (bin/pos-communication-matrix-listener): systemd user daemon (pos-matrix-listener.service) long-polling/sync(30s timeout, per-syncsincetoken, compact filter dropping presence/account_data/device noise,m.room.messageonly); reacts toMATRIX_USER_ID's own messages (resolved via/account/whoamiif unset),MATRIX_ROOM_IDrestricts to one room;/and!both resolve; replies threadedm.in_reply_to;@quietno-reply marker;/cmd::desc=…map descriptions;ai …bridge (pos ai gemini ask, per-room sessionmatrix-<room>,ai /resetclears, markdown stripped); interactive editor (--status/--enable/--disable/--run), 60s command timeout, exit-code prefix, ~3800-char truncation.communication-matrix-listeneradded toINTERACTIVE_CMDS(stdin editor + forever-loop daemon). Docs: POS.md rows + "in detail" sections + ai bridge note, howto/communication.md rewritten Matrix sections, HOWTO.md index + config table + platform note,bin/posusage EXAMPLES;make gen && make checkgreen. Verified against a mock homeserver: send plain/markdown/--room/test request shape (URL-encoding, Bearer auth, JSON body), login token save, listener owner-filter +/statusreply +/help+@quietsilence + non-zero exit reply + interactive editor add. — state-based threshold rule monitors (eventer). Each line of~/.config/linux_post_install/event.envis an independent rule:["<msg>" if ] <check-command> <op> <threshold>(op> < >= <= == !=, unit suffix ok60c/80%). The check command is run on every pass and its first numeric output compared float-safe; operator detected as the rightmostop thresholdpair so checks containing their own>/<(awk, redirection) parse fine. Alerts once on false→true plus one recovery message on true→false (no repeats while a condition holds); per-rule state in~/.local/share/linux_post_install/eventer/state/keyed by rule-line hash (editing a rule resets its state). Subcommands:run(timer entrypoint),config(interactive add/remove/edit with validation by test-running the check),list(rules + live values),enable [interval](systemd user timerpos-event-trigger.timer+ oneshot service;5m…weeklyorOnCalendar=…; graceful warnings when no user systemd manager,loginctl enable-lingerattempt),disable,status.--dry-runhonors the DEV.md dry-run convention. Alerts vialib/notify.sh(Telegram default; other platforms viaNOTIFY_PLATFORM). New:bin/pos-system-event-trigger,lib/eventer-lib.sh,config/event.envtemplate (installed no-clobber by postinstall),lib/eventer-lib.shinstalled by install.sh,system-event-triggeradded toINTERACTIVE_CMDS, usage EXAMPLES row. Docs: POS.md system row, HOWTO.md index row, howto/event-trigger.md;make gen && make checkgreen; functional tests covered trigger/recovery/no-repeat, float + unit parsing, editor add/remove/edit + validation + dry-run, timer enable/disable/status (graceful), dispatcher routing. -
2026-08-09 —
pos media mp3/mp4hardened + smart format selection. Both tools: yt-dlp calls go throughspawn(honor$DRY_RUN;--dry-runprints the exact command and skips dep checks),-o/--output,--no-playlist,--cookies(file existence check), clean ffmpeg/yt-dlp guards,# POS_FLAGS:for completion, full metadata (--embed-metadata --embed-chapters --embed-thumbnail --no-overwrites, mp3 also--convert-thumbnails jpg+--parse-metadata "%(artist,uploader)s:%(artist)s"so the uploader fills the artist tag). mp3 gains--by-artist(~/Music/<artist>/<title>.mp3). mp4 gains-f <id>/--best/--worst(no prompt), conflict validation, and an interactive picker that shows a curated-Ftable ([audio]/[video]/[combo]grouping, raw clutter dropped) on stderr — stdout carries only the chosen id (ui_pick lesson) — with id validation against the real table and empty/best default. Docs: howto/media.md rewritten (flags, metadata, by-artist, troubleshooting);make gen && make checkgreen. -
2026-08-09 — Telegram
ai …now answers about a message you reply to: the listener extractsreply_to_message.text(falls back tocaption) from each update and passes it tohandle_message; the AI bridge prefixes the prompt with[Reply context — the message you are replying to]. So replying to a/statusoutput and askingai check this detailsgives the model the actual output. Applies only to the AI bridge (mapped/commandsuntouched); reply context rides in the user turn so the session records what was analyzed. Docs: howto/ai.md bridge section. -
2026-08-09 —
pos ai geminisessions + Telegram-friendly replies.--session <name>givesask/chatpersistent memory (~/.local/share/linux_post_install/ai/<name>.json, capped at 40 turns, pruning keeps the first user turn as scene); newsessionssubcommand (list /reset <name>). Telegram listener now keeps one session per chat (telegram-<chat_id>) withai /resetto clear. New--system "<text>"flag injects a GeminisystemInstruction(viajqmerge) sent every turn but never stored in the session file; the listener passes a Telegram-voice prompt ("reply like a friendly Telegram chat, use emojis") and strips markdown (**,*, backticks,#, links, lists, blockquotes) from replies beforesendMessage, since messages go out as plain text. Docs: howto/ai.md (flags, sessions, bridge memory/formatting),make gen && make checkgreen. -
2026-08-09 — Fixed
pos configsecret-value corruption:cfg_read_secret's cursor-advanceechowent to stdout and, since the function is called via$(...), a leading\nended up inside every secret value → the env file gotAI_GEMINI_API_KEY="\n<key>", unreadable bycfg_value/load_config(menu showed(not set),pos ai geminidemanded a key). The newline now goes to the terminal (echo >&2). Defense in depth:cfg_write/write_config_keystrip CR and truncate multi-line pastes (warn),cfg_valueand the ai/telegramload_configs strip CR on read. Verified on a real PTY (piped tests couldn't reproduce — non-TTY stdin skips the echo path). -
2026-08-09 —
aicategory —pos ai gemini(ask/chat/models) via Google Gemini REST API.askprints only the answer (pipe/script/Telegram-friendly),chatis a multi-turn REPL (q/quit/Ctrl+C,/reset, empty input re-prompts),modelslists generateContent-capable ids and flags the default;--modeloverride; defaultgemini-2.5-flash. Config scopeai(AI_GEMINI_API_KEYsecret +AI_GEMINI_MODEL) in~/.config/linux_post_install/ai.env, edited viapos config ai;config/ai.envtemplate installed no-clobber by postinstall;ai-geminiadded toINTERACTIVE_CMDS. Telegram listener now answers non-command messages starting withaiviapos ai gemini ask(owner chat only; error replies carry thepos config aihint) — future intents (reminders) slot in as more case arms inhandle_message. Docs: POS.mdaisection + listener bridge, howto/ai.md, HOWTO/README index rows,bin/posusage example. -
2026-08-09 — Entertainment plugins
gold+weathernow emit emoji-visualized Telegram messages. Gold: headline is USD/gram (XAU/oz ÷ 31.1034768), ounce as reference, bid/ask, cleaned timestamp (+00:00/fractional seconds stripped). Weather: per-WMO-code emoji (☀️/🌙 day-night aware for clear sky), °C + feels-like, humidity, wind with unit spacing. Both verified live; emojis are safe in the default plain send mode. -
2026-08-09 —
pos tree: prints the liveposcommand tree (categories → commands → subcommands) by deriving the hierarchy frombin/pos-*filenames +# POS:/# POS_SUBCMDS:headers, so it always matches what the dispatcher can run. Category-less likepos-config;--depth Nlimit;pos help treeworks. Docs: POS.mdtreesection,bin/posusage example,make genregenerated the AGENT_Context tree/dispatch/filetable +_pos_flags[tree]. -
2026-08-09 — Telegram sender
config/config setremoved — redundant withpos config telegram(same# POS_CONFIG:registry, masked token display + input, chat-id validation, chmod 600); sender/listener error hints now point there. Deep-review bugfixes in the same commit: mapped/commandvalues containing|are no longer truncated (load_mapswitched from a|to a\x1fdelimiter — previously/up=echo hi | headsilently ranecho hi);pos entertainment send <plugin> [args…]actually forwards the extra args (every arg wasshifted in the flag loop, so$@was empty) and passes--before the message so leading--plugin output isn't parsed as an option;write_config_key(entertainment-lib) andcfg_write(config-ui) replaced unescapedsed -i "s|^K=.*|K=\"$v\"|"with grep-v+append so values with&/|/\no longer mangle (also the path all telegram config now flows through);sync_systemddaemon-reloads after removing timer units;digitsconfig validation accepts negative group/supergroup chat ids (-100…). -
2026-08-09 — Fixed telegram listener editor crash on remove/edit/test:
ui_pickprinted its menu listing to stdout, soidx="$(ui_pick)"captured the menu and the number, andMAP_CMDS[$idx](arithmetic array subscript) blew up with "syntax error in expression". Menu decoration now goes to stderr; only the picked index is emitted on stdout. Pre-existing bug (before the::descwork), exposed by the description column. -
2026-08-09 — Telegram listener pushes its mapped
/commandsto the bot's/menu viasetMyCommands(auto after every map edit, on--enable, and at daemon start; manual--sync-commandsflag). Map lines may carry a menu description:/cmd::short description=bash command(falls back to the bash command, ~40 chars). Names are validated against Telegram's lowercase[a-z0-9_]rule — invalid ones are skipped from the menu with a warning but still resolve when typed; empty map clears the menu. Fixed latent bugs found by the sync work:map_has(awkEND{exit 1}overrode the match), andwarn()went to stdout so it leaked into the generated JSON (now stderr). -
2026-08-09 —
pos config <TAB>scope completion is now cached at gen time (_pos_config_scopesarray emitted bymake genfrom the# POS_CONFIG:registry) instead of scanning ~40 tools per TAB — a per-keypress subshell storm that wedged interactive shells for minutes on the loaded homelab box. Two stuck-bashsessions (69%/38% CPU) killed.plugin_marker/plugin_keyshardened with|| truesoconfig_keysno longer aborts mid-scan underset -euo pipefailon mixed lib/plugin dirs (installed layout) — fixes missing plugin keys inpos config entertainment. -
2026-08-09 —
pos config <scope>interactive config editor: reads the# POS_CONFIG:registry across tools into a single runtime config (~/.config/linux_post_install/*.env, one file per scope, chmod 600); secret masking with show/hide toggle,digits:/num:/url:validation,-to clear, blank keeps;*pluginsmarker expands plugin vars (entertainment) fromentertainment-lib.sh;desc::examplevalue-format hints shown in the editor; gen-docs now handles category-less tools (pos-config), fixed aset -e+pipefailbug that truncated the header registry. -
2026-08-09 —
pos-communication-telegram→pos-communication-telegram-sender: one canonicalsend(dropped the legacy--sendflag, which duplicated thesendsubcommand in completion).pos communication telegram <TAB>now completes to justsender listener.lib/notify.shmaps platformtelegram→telegram-sendervianotify_sender_name(); entertainment-send + health--sendcheck updated. Removed phantom subcommands from howto/communication.md (webhook/logs/broadcast/file never existed). -
2026-08-09 — Structure/convention audit fix:
--dry-runnow truly dry (spawn()honorsDRY_RUN, install.sh exports it to child phases, postinstall mutations run-wrapped);gen-docs.shno longer chmods regenerated files to 0600;make checknow syntax-checks apps/entertainment/features/templates;.gitignoreprotectsconfig/authorized_keys+config/rclone.conf; honest--sendconfirmation; docs refreshed (notify.sh in lib lists, pos-health systemd units, tsui, scripts/, INTERACTIVE_CMDS). -
2026-08-07 —
pos system health --sendnotification-only; listener@quietprefix (run mapped command without replying, for commands that self-notify)./status=@quiet pos system health --send= exactly one digest. -
2026-08-12 —
pos network download(bin/pos-network-download) — aria2 JSON-RPC daemon + queue control. Daemon: persistentaria2cas a systemd user service (pos-aria2.service,${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user,enable --now+ linger warning on headless boxes),--rpc-listen-port=6800, generatedRPC_SECRETin~/.config/linux_post_install/download.env(chmod 600, env override), unit flags--continue=true --max-connection-per-server=16 --split=16 --seed-time=0 --dir=$HOME/Downloads. Commands (18):start/stop/status(+ bare overview = status+list),add <url…> [--dir --out --split --tmux],torrent <file|magnet…> [--dir --seed --tmux](base64addTorrent),metalink <file|url> [--tmux],list(active/waiting/stopped table),info/files/peers <gid>,pause|resume|remove [gid|all](--force→force*),purge,move <gid> <pos>,limit [gid] <speed>(--upload, 0=unlimited,2M/512K),set <k=v…> [--gid],watch [gid](2s live repoll; exits when that gid completes).--tmuxopens a detacheddl-<name>session runningwatch <gid>(name from--out/URL basename, sanitized, 40-char truncate,-2on collision; closes itself on completion). Deps:aria2c/jq/curlguards before--help;aria2added to preinstall PACKAGES. No stdin → not inINTERACTIVE_CMDS. JSON built withjq -nc --arg(never string interpolation — fixes JSON-quote bugs);# POS_SUBCMDS:(18) +# POS_FLAGS:→ completions. Test seamsRPC_PORT/RPC_SECRET/DOWNLOAD_DIR/USER_SYSTEMD_DIR/ACTIVE_MARKER; 76-case stub-PATH behavior suite green (unit content, secret 600, add→gid, tables, queue ops, error paths). Docs: POS.md network row+detail, howto/network.md section, HOWTO index, AGENT_Context Common Tasks row.make gen && make checkgreen.
Now
- (none — Tier 1 shipped:
pos system health,lib/notify.sh, digest timer)
Next
- Wire alerting into more tools as they are added (default: source
lib/notify.sh, callnotify_sendon success/failure).
Later — idea backlog
- Tier 2: watch plugins —
pos system watch <event>: poll conditions and alert on change (public IP changed, disk > 90%, backup skipped, fail2ban spike). Reusesnotify_send+ a systemd timer per watch. - Tier 2:
pos healthextras — temperature/fan/load average thresholds,ss -tlnport checks for known services, SMART status for disks. - Tier 3: backup rotation + remote target — keep-N rotations, upload to
rclone remote after verify,
--remoteflag, digest reports rotation age. - Tier 3:
pos secretvault — gpg/age-encrypted key-value store; backend for future tools that need stored tokens. - Tier 3:
pos inventory— machine manifest (OS, packages, services, mounted disks, USB devices) exportable as markdown/JSON. - Tier 4:
pos self update— pull repo,make gen && make check, re-run install.sh to refresh/usr/local/bin. - Tier 4:
pos new— scaffold a new tool fromtemplates/pos-tool.sh(category, name, POS header, exec bit, doc stubs). - NOT NOW: per-category
bin/subdirectories — flatbin/+ filename dispatch scales fine; revisit only ifbin/passes ~40 files. - NOT NOW: split
lib/entertainment-lib.sh— fine under 600 lines; revisit if it grows.
Done (summary, newest last)
- 2026-08-06: Fix entertainment timer
1hnot firing —interval_to_oncalendaremitted invalidOnCalendar=*-*-* */N:00:00(systemd rejects*/Nin the hour field); now*-*-* 00/N:00:00. Dropped the cron fallback entirely: scheduling is systemd user timers only (sync_cron/interval_to_cron/cron_blockremoved),statussimplified,Ndintervals rejected with a clear error. - 2026-08-06: Nested
possubcommands —# POS_SUBCMDS:header annotation (telegram, docker-compose, docker-vbox) +make genemits a_pos_subcmdscompletion map; nested tools (telegram listener) auto-list under their parent instead of as a flat sibling (telegram-listener) inpos <category>and tab-completion; generic tool-level completion (subcommands + flags +--help). - 2026-08-06: Telegram listener —
pos communication telegram listener: interactive/command→ bash map editor + owner-only polling daemon as a systemd user service (map in~/.config/linux_post_install/telegram_commands.env, re-read per message;/help, unknown-command reply, 60s timeout, stdout reply). - 2026-08-06: NFS in
pos system—pos system nfs-server(status/share/ unshare/list/reload/enable/disable, idempotent /etc/exports edits, generic default with Tailscale/WireGuard/LAN examples) +pos system nfs-client(mount/unmount/list + persistent mounts as systemd.mountunits ordered after network-online.target, no fstab);nfs-kernel-server+nfs-commonadded to preinstall PACKAGES. - 2026-08-06:
posHOW-TO guide set —DOC/HOWTO.mdindex + per-categoryDOC/howto/*.md(network, docker, media, system, ssh, usb, communication, entertainment) with flags, recipes, config, and troubleshooting; wired into DOC/README, root README, AGENTS.md. - 2026-08-06: Multi-platform alerting —
lib/notify.shroutes viaNOTIFY_PLATFORM(notify.env, default telegram; sender contract for Matrix/Synapse later),system.envshared config for health/backup, dynamic effective values in--help, telegram--markdownalias. - 2026-08-06: Tier 1 —
pos system health(dashboard +--send),lib/notify.sh(wired into backup + firewall), daily digest timer via postinstall. - 2026-08-06: Document Map index + Entertainment section in AGENT_Context (
cf36780). - 2026-08-06: Entertainment module — plugins (weather/joke/gold),
pos entertainment config/enable/disable/send/status, auto-trigger + Telegram send. - 2026-08-05:
pos communication telegram—--parse-mode(plain/markdown/html). - 2026-08-05: doc/code sync gate —
make gen+make check+ pre-commit hook. - 2026-08-05:
pos usb server— USB Redirector control tool (494eae2). - 2026-08-05:
pos <category> --helpauto-discovery in the dispatcher. - 2026-08-05: AGENTS.md with lazy-loaded DOC references.