# Sovereign AI workstation operator kit I use this as a starting point for an operator's own rules and scripts. The paths and names below are placeholders, not details of a deployed machine. Adapt them, review the commands, and test with harmless work first. This kit describes a build pattern, including proposed n8n workflows; it does not claim that every integration is installed or finished. The accompanying article's Fact-check notes and sources links to official documentation. ## My operating rules I check capacity before starting independent work: ```bash nproc uptime free -g ``` I keep parallel work below roughly half the available cores and leave at least 20 GiB available on a large workstation. These are example admission limits, not product requirements. I reduce concurrency when memory or load crosses my limits. On smaller machines I choose smaller budgets. I divide work by target or repository. Each job owns its working folder, branch, brief, result and scripts. Shared scripts are read-only or copied before editing. I run independent gates together, then wait for every exit status. A failed gate blocks deployment. I serialize production deploys per site, pushes per repository and access to a person's desktop browser. Git worktrees let independent branches have separate files. I fetch the current main branch before merging and pushing, without rewriting shared history. A worktree is not a sandbox: each gate still needs its own build output and temporary directory. I save a checkpoint after each unit. A restart reads those checkpoints and checks whether the input changed before reusing the result. My capacity limits, browser queue and origin limiter are shared across jobs, not reset inside each worker. ## A launcher with progress and dependency records I save this example as `$OPS_ROOT/run-job.sh`. It runs a foreground command inside a private job folder. The caller supplies a unique job name and an optional dependency name. Each invocation keeps its own status directory. Dependencies must be queued before dependents. Reusing a completed name is refused, so a new run cannot silently inherit an old success. ```bash #!/usr/bin/env bash set -euo pipefail umask 077 root="${OPS_DIR:?Set OPS_DIR to your private operations directory}/jobs" mkdir -p "$root" job=${1:?Usage: run-job.sh JOB DEPENDENCY_OR_NONE COMMAND ARG...} dep=${2:?Missing dependency or none} shift 2 [[ $job =~ ^[a-zA-Z0-9_-]+$ ]] || exit 2 [[ $dep == none || $dep =~ ^[a-zA-Z0-9_-]+$ ]] || exit 2 [[ $job != "$dep" && $# -gt 0 ]] || exit 2 mkdir "$root/$job" || { echo 'Use a fresh job name' >&2; exit 2; } progress="$root/progress.tsv" record() { ( flock 9 printf '%s\t%s\t%s\n' "$(date -u +%FT%TZ)" "$job" "$1" >&9 ) 9>>"$progress" } heartbeat_pid='' finish() { status=$? trap - EXIT if [[ -n $heartbeat_pid ]]; then kill "$heartbeat_pid" 2>/dev/null || true wait "$heartbeat_pid" 2>/dev/null || true fi printf '%s\n' "$status" > "$root/$job/exit.tmp" mv "$root/$job/exit.tmp" "$root/$job/exit" record "exit:$status" exit "$status" } trap finish EXIT record queued ( while :; do date +%s > "$root/$job/heartbeat.tmp" mv "$root/$job/heartbeat.tmp" "$root/$job/heartbeat" sleep 30 done ) & heartbeat_pid=$! if [[ $dep != none ]]; then [[ -d $root/$dep ]] || { echo 'Queue dependency first' >&2; exit 2; } deadline=$((SECONDS + 3600)) until [[ -f $root/$dep/exit ]]; do (( SECONDS < deadline )) || exit 124 sleep 2 done [[ $(cat "$root/$dep/exit") == 0 ]] || exit 3 fi record start cd "$root/$job" "$@" > output.log 2>&1 ``` Example use after making the launcher executable: ```bash chmod 700 "${OPS_DIR:?Set OPS_DIR to your private operations directory}/run-job.sh" "${OPS_DIR:?Set OPS_DIR to your private operations directory}/run-job.sh" build-example none bash -c 'printf "build complete\n"' & build_pid=$! "${OPS_DIR:?Set OPS_DIR to your private operations directory}/run-job.sh" check-example build-example bash -c 'printf "check complete\n"' & check_pid=$! failed=0 wait "$build_pid" || failed=1 wait "$check_pid" || failed=1 exit "$failed" ``` The heartbeat proves that the supervisor is alive, not that useful work is advancing. A worker must also write checkpoints or update a progress counter. If a worker needs retries, it records each attempt and never converts a failure into a successful dependency exit. For durable launch management I run the launcher under a user service so logout does not stop it. I do not put passwords in command arguments or command output. ## Watchdogs and alerts My watchdog checks both heartbeat freshness and time since the last useful checkpoint. It also checks failed exit records and queues that have stopped draining. A stale heartbeat marks the job suspect; it does not automatically kill or restart a production operation. A new failure creates an alert; an unchanged failure does not send the same alert each minute. Recovery creates one recovery event. This small heartbeat checker produces local output for a separate alert queue. Save it as `$OPS_ROOT/watch-jobs.sh`: ```bash #!/usr/bin/env bash set -euo pipefail umask 077 root="${OPS_DIR:?Set OPS_DIR to your private operations directory}/jobs" mkdir -p "$root" now=$(date +%s) shopt -s nullglob for dir in "$root"/*/; do [[ -f ${dir}exit ]] && continue [[ -f ${dir}heartbeat ]] || continue stamp=$(cat "${dir}heartbeat") [[ $stamp =~ ^[0-9]+$ ]] || continue if (( now - stamp > 180 )); then if [[ ! -f ${dir}stale-alert ]]; then printf '%s stale supervisor: %s\n' "$(date -u +%FT%TZ)" "$(basename "$dir")" touch "${dir}stale-alert" fi elif [[ -f ${dir}stale-alert ]]; then printf '%s supervisor recovered: %s\n' "$(date -u +%FT%TZ)" "$(basename "$dir")" rm -- "${dir}stale-alert" fi done ``` I serialize this checker or lock it to avoid duplicate alerts. A production checker also covers a queued job with no heartbeat and a stalled worker with a healthy supervisor. I send only sanitized summaries to an approved destination. I keep delivery failures in a durable outbox and retry with backoff. No automatic email, chat message or other outward action is authorized by this example. ## The complete fetch chain and honest access reporting For every planned public page I try a direct read and then my approved proxy chain, which tries direct, Cloudflare worker and Deno egress. I do not skip the chain because another page worked or because I have enough examples. If the chain is blocked, I use a passive stealth browser render: load, wait and read. I do not click a challenge, login, payment or consent-to-proceed control. I treat an HTTP 403, 429 or 503, an empty or tiny response, a challenge title or form, and a CAPTCHA page as blocked. I inspect the actual page; a challenge-platform script on a normal page is not enough to classify it as blocked. I never solve a CAPTCHA, bypass a login or paywall, or impersonate a search-engine crawler. I retain the UTC timestamp, requested URL, final URL, status and block assessment for every attempt. From the proxy I retain `egress`, `egressTried`, `egressTimedOut`, `direct.status`, `direct.blocked` and `finalUrl`. I record elapsed time and error separately. Only after direct, chain and passive render all fail do I mark a page unmeasured. I report direct access blocking as its own access finding: which requesting environment was blocked, what that prevents and what policy change the site owner could consider. I do not infer that every AI tool or every datacenter is blocked from one request. Content findings use the usable page returned by the chain or render. Coverage lists planned pages, measured pages, the tier used for each, and every unmeasured page with all attempts and reasons. My shared cache keys include the URL, relevant request representation and freshness policy. It respects HTTP cache directives and daily measurement policy. It stores usable content only. A blocked answer goes into a separate attempt log, never into the content cache. A cached answer is labeled with its observation time; an audit that requires a current measurement forces a refresh. A central limiter allows roughly one request per second per origin, including browser and proxy reads, and also honors the proxy's total budget and Retry-After responses. ## Shareable-report gate Before sharing an article, report, PDF or bundle I scan its text, metadata, link annotations, image captions and HTML href/src values. I remove private machine paths, local URLs, draft preview URLs, internal job names, evidence filenames, client identifiers and secrets. The generic example paths in this kit are instructions to adapt, not evidence citations. I cite a public source URL, observation date and UTC time, and a short excerpt where it supports a finding. Screenshots needed by the reader are embedded in the report itself. Captures, raw logs, credentials and scripts used for measurement stay private and are excluded from shared bundles. I describe methods plainly, such as a proxy fetch or headless browser render. A PDF scan covers extracted text and link annotations; an HTML scan covers both visible text and all URL attributes. Any leak fails the gate until corrected. ## Scheduled monitoring and persistent services I use systemd user services for persistent work. This example service runs a reviewed, executable monitoring script. Save the unit in your user systemd unit directory as `workstation-monitor.service`: ```ini [Unit] Description=Workstation monitoring checks [Service] Type=oneshot ExecStart=%h/ops/monitor.sh WorkingDirectory=%h/ops UMask=0077 TimeoutStartSec=10min NoNewPrivileges=yes ``` Save the timer alongside it as `workstation-monitor.timer`: ```ini [Unit] Description=Schedule workstation monitoring [Timer] OnCalendar=hourly Persistent=true RandomizedDelaySec=2min Unit=workstation-monitor.service [Install] WantedBy=timers.target ``` After writing and testing `$OPS_ROOT/monitor.sh`, I validate and enable the timer: ```bash systemd-analyze --user verify "${USER_UNIT_DIR:?Set USER_UNIT_DIR to your user unit directory}/workstation-monitor.service" "${USER_UNIT_DIR:?Set USER_UNIT_DIR to your user unit directory}/workstation-monitor.timer" systemctl --user daemon-reload systemctl --user enable --now workstation-monitor.timer systemctl --user start workstation-monitor.service systemctl --user status workstation-monitor.service workstation-monitor.timer systemctl --user list-timers loginctl show-user "$USER" -p Linger ``` An administrator enables linger for the chosen operator account with `sudo loginctl enable-linger "$USER"`, after confirming that persistent user services are intended. Linger allows the user's service manager to run without an active login. For a continuous worker I use `Type=simple`, `Restart=on-failure`, a modest `RestartSec`, and `WantedBy=default.target`; I enable that service too. The script handles termination and writes checkpoints. I keep private rotating logs in `$OPS_ROOT/logs/` and check the journal when a service fails. I never copy a secret into a unit or journal. My timer checklist covers the timezone, missed-run behavior, overlap lock, timeout, exit status, fresh baseline and alert route. I test both failure and recovery. I compare normalized content with the previous usable observation and alert on a meaningful change, rather than every successful poll. An inaccessible page is an access event, not evidence that its content changed. I monitor disk space, backup age, queue age and service health as separate checks. ## Chat and email dispatcher acceptance contract Use the [full operating pattern](https://jwatte.com/blog/sovereign-ai-workstation-operating-system/) as the design reference. A small business can request status, a publisher can request indexing preparation, and an agency can request a public-page audit. These are example uses, not measured customer results. Wire Email Trigger (IMAP) or Telegram Trigger to a reviewed Code validator, then SSH to a dedicated dispatcher. Use Send Email or Telegram only for fixed-owner replies that passed the shareable gate. All channels stay inactive, with sender policy disabled, until the owner adds credentials in the workflow credential store and proves both acceptance and rejection. Keep credential values out of exports, prompts and logs. | Gate | Required acceptance | |---|---| | Identity | Exact allowlisted sender; private chat identity; email DMARC pass aligned with From and DKIM pass from a trusted receiver that strips sender-supplied authentication headers | | Input | Message is data; reject unknown fields, oversized input, shell syntax, unknown commands and unexpected arguments | | SSH | Dedicated forced-command key with restrict; only the dispatcher can run; no shell, PTY or forwarding; never evaluate SSH_ORIGINAL_COMMAND | | Dispatcher | Fixed status, jobs, audit, gsc-prep, gsc-check, gsc, stop and confirm verbs; public HTTPS and redirect checks; ownership checks; no deploy, push or arbitrary-send verb | | Agent | Sandbox or fixed tools with a restricted account; never skip-permissions; fixed prompt and validated capture data; refuse out-of-scope work | | Load and stop | Persist rate limits, cooldown, concurrency bounds, timeouts, deduplication IDs and kill switch across restart | | Audit | Append-only transitions and rejection records; no secrets or sensitive message bodies; preserve history through rotation | | Approval | Expiring one-time code bound to sender, channel, exact action, payload and destination; atomic consumption and replay rejection before outward action | | Delivery | Final privacy and secret gate covers reply, attachment and archive members; PDF text and annotations; HTML links and sources; fixed destination | Treat a status or audit reply as outward-facing too. Preview its exact gated content in the owner's private review interface and require confirmation before sending, or leave it unsent for owner retrieval. A variant that confirms only stop and indexing clicks has not met this stricter delivery contract. Do not describe such a variant as confirming all outward actions. Test valid status and public audit inputs, disallowed senders, forged email authentication, group chat, unknown commands, injection, unsafe redirects, private targets, wrong-owner job IDs, expired confirmation, replay, duplicate delivery and the kill switch. Run manual tests without live sends, then repeat after service restart. A workflow importing successfully is not execution evidence. Keep every attempt's receipt distinct. The reviewed dormant integration passed manual channel execution and restart checks; owner credentials and activation remain separate, and its ordinary replies still need the stricter confirmation gate above. ### Owner browser handoff: gsc-check then gsc gsc-prep measures public candidates and prepares timing and the attempt ledger without clicks. The owner signs the desktop browser in to the intended search property and completes account challenges; the agent never handles the credential. gsc-check verifies the correct connected desktop and property without spending quota. Stop if the intended desktop is absent, even when another computer is connected. gsc requires fresh readiness, a prepared scoped plan and same-sender one-time confirmation before indexing clicks. Recheck timing and the accepted/refused ledger before starting and between slots. Record each outcome and stop on refusal, unknown state or changed page. Do not replay armed work after reboot. A manual indexing request still needs the owner's signed-in browser; public preparation is not an API replacement. The reviewed integration had not yet proved Linux property access or first-click-stop behavior. ### Live desktop promotion checklist 1. Record the previous digest, configuration, profile volume and effective runtime UID/GID. Bound proof runs and promotion attempts before starting. 2. Make an isolated copy of the live profile with numeric ownership, modes and volume layout preserved. Test the already-built candidate there, never against the live volume. Check any narrow ownership repair on the copy first and preserve modes. 3. Validate generated health-check quoting through Compose rendering and actual container execution before promotion. Give each hook call a unique receipt. A healthy container or a clean-profile pass alone is insufficient. 4. Require real CLI and browser checks after start and after a second start caused by restart. Restore the test copy's original profile after proof. Promote only that identical immutable image digest. 5. Obtain authorization for an active desktop interruption. Close the browser gracefully, stop the desktop and snapshot the profile volume before replacement. Read back and checksum the archive; retain it privately with the old image and configuration. 6. Promote through the managed persistent definition. Verify digest, effective user, mounts, health, CLI behavior and retained state after start and again after a second restart. Check signed-in browser readiness separately rather than inferring it from installed extensions. 7. Automatically roll back image, configuration and profile if either live check fails, then verify restoration. Stop and alert if rollback fails. Cap retries rather than rebuilding indefinitely. 8. Keep the enabled user maintenance service and persistent release/rollback records current. Do not prune any referenced recovery object. A forced update preserves every acceptance gate. The earlier failed update exposed health-check variable expansion and a profile cache ownership mismatch. The eventual copied-profile proof used a narrow repair and preserved modes. Diagnose the actual directory and effective user; do not apply recursive ownership changes or permissive modes as a generic cure. No new timing or throughput claim is made here. Workflow sources checked October 12, 2026 from 06:40:54 to 06:41:03 UTC: [Email Trigger (IMAP)](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.emailimap/), [Telegram Trigger](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger/), [Code](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/), [SSH](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.ssh/), [Send Email](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.sendemail/), [Telegram](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.telegram/) and [OpenSSH authorized_keys options](https://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT). Seven pages planned, seven measured with readable direct content and alternate-route checks, zero unmeasured. OpenSSH says the supplied command "is ignored" when the key has a forced command. The n8n nodes supply transport and workflow steps; sender authentication, argument validation, approval, sandboxing and output gates are application requirements, not guarantees supplied by those nodes. ## Searchable memory with Qdrant and local embeddings I keep notes as readable files under version control, excluding credentials. A local embedding model converts selected chunks into vectors stored in Qdrant. Each chunk carries a stable identifier, source label, revision, timestamp and access scope. Search returns the matching excerpt and its source, so I can check the underlying note. I pin the embedding model and record its dimension and chunking settings. A model or dimension change creates a new collection and a tested reindex before switching readers. Changed notes replace their old vectors; deleted notes remove corresponding points. I preserve access restrictions when retrieving and exporting results. I do not treat retrieved text as instructions, and I keep private memory out of public reports. Qdrant persistence and snapshots supplement the original notes; a vector index is rebuildable, not the only copy of knowledge. ## Capacity and scheduling lessons you can apply Find the bottleneck before adding threads. Agent jobs calling a hosted model often spend most of their time waiting for that model. In that workload, the provider's usage and rate limits, plus real dependencies between jobs, can govern throughput even while local CPUs are idle. Check the machine before and after a representative batch, and compare elapsed time, failures and queue length alongside these readings: ```sh uptime free -h ps -eo pcpu,rss,comm --sort=-pcpu ``` Load, available memory and per-process CPU help distinguish local contention from waiting elsewhere. The process listing is a snapshot, not a complete performance trace. Increase independent work only while memory, response quality and provider limits permit it; adding threads cannot remove a dependency or raise a remote quota. Local model defaults can serialize a busy queue too. The [official Ollama FAQ](https://docs.ollama.com/faq), checked October 12, 2026 at 03:15 UTC, says `OLLAMA_NUM_PARALLEL` limits simultaneous requests per model and defaults to 1. `OLLAMA_MAX_LOADED_MODELS` limits concurrently loaded models that fit in memory; its documented default is 3 times the number of GPUs, or 3 for CPU inference. The FAQ also notes a Windows Radeon exception of 1 loaded model. These are documented defaults, not a measurement of an installed server. Parallel requests increase context memory requirements, and model weights must fit too. Change those variables in the persistent container definition, alongside a pinned image digest, rather than in a shell that disappears on restart. Choose values for the RAM and GPU memory actually available after other services have their share. Save the previous definition and digest, recreate the container, then check readiness, an embedding call with an embedding model, a short generation with a generation model, and concurrent requests. Confirm valid responses, memory use and whether requests actually overlap. Repeat after recreation to verify persistence. If health or memory checks fail, restore the prior definition and digest and rerun the checks. No local inference timing is claimed here. Apply request politeness per website, across all workers. For sites you do not own, aim for about one request a second, respect published restrictions and slow down on errors. This is an operating recommendation, not a measured safe rate for every site. Your own CDN-hosted sites can use a higher measured rate when their capacity allows it, with bounded concurrency, backoff on 429 or 503, and respect for `Retry-After`. Ownership does not remove limits on an upstream service or a shared proxy. Make nightly checks incremental without losing visibility. Always read the full sitemap, then check new pages, changed pages, previously failing pages and a rotating slice of the rest. Use sitemap modification dates as a signal, not proof that everything else is unchanged. Run a full sweep weekly. Every report should state pages planned, pages measured, the access tier used, unmeasured pages with reasons, and what was deferred by the incremental policy. A green subset must not imply that the whole site passed. Two scheduling traps deserve an explicit gate. A maintenance task waiting for no agent jobs, while a queued agent waits for that maintenance task, creates a circular wait. Put the waiting in a plain shell loop outside the agent job accounting, and start the agent only after the gate opens. Check the condition again when acquiring the gate so another launcher cannot race it. Use a timeout and a visible failure state instead of an endless silent wait. A job's exit also does not prove its work finished when it hands work to a background service. Gate the next step on that service's own terminal state and result, tied to the submitted work identifier. A timer being enabled, a process being alive or a submission being accepted is not completion. Failed or unknown states should stop dependent work and preserve evidence for review. Order destructive cleanup after the update has finished and created verified rollback points. Wait for the service's own successful completion and acceptance checks, then prune only objects outside the retention policy. Keep every image, configuration and backup referenced by current rollback pins. Resolve those references before deletion; if they cannot be resolved, stop cleanup. This keeps an update from losing its recovery path while it is still running. ## Local bulk triage with Ollama I use a local model for repetitive first-pass classification: grouping monitor changes, labeling documents and proposing summaries. A batch worker submits bounded chunks to Ollama, validates the response against an expected schema and stores the input revision, model identity and decision. Invalid responses or uncertain cases go to a human or a more capable reviewer. The local model cannot approve a deploy, access secrets or execute commands from a document. I measure queue delay, memory use and output quality on my own samples before raising concurrency. I keep sensitive prompts and outputs private. Local inference changes where processing runs; it does not remove the need for permissions, input validation or review. ## Browser measurement and parallel deploy gates I add a bounded headless browser pool for Playwright checks, Lighthouse runs and screenshot comparisons. Each worker gets an isolated context, a private output folder and a fixed viewport. I pin browser versions and keep fonts, timezone and test inputs consistent. I compare a page with itself first to find unstable regions before treating a visual difference as a regression. I run browser work across different origins while sharing the origin limiter. I queue work when the browser budget is full. Screenshots and Lighthouse findings support a measured check, not a blanket accessibility or performance certification. A person reviews uncertain changes. I keep a person's desktop browser separate from this pool. For deployment I run style, internal-link, privacy and secret gates in independent workspaces, collect their statuses and proceed only when all pass. The draft is checked before production. Production deployment and repository pushes each have a per-target lock. I verify the live content, downloadable files, headers and key assets after deployment, then confirm the intended commit is on the remote main branch. ## Backup layers: notes, local snapshots and off-site copies Git preserves reviewed note and script history. It is not a backup of every file, and sensitive material stays excluded. Restic adds encrypted snapshots to a local repository and a separate off-site repository. I protect repository passwords outside repositories and logs. I include application data through consistent snapshots or application-supported dumps, not a blind copy of a live database. These examples assume each restic repository has already been initialized and its repository location and password file are provided securely. Replace the generic data paths with an explicit, reviewed inclusion list: ```bash : "${RESTIC_REPOSITORY:?Set a protected repository location}" : "${RESTIC_PASSWORD_FILE:?Set a protected password file}" restic backup "${OPS_DIR:?Set OPS_DIR to your private operations directory}/notes" "${OPS_DIR:?Set OPS_DIR to your private operations directory}/config" --tag workstation restic snapshots --tag workstation restic check restore_dir=$(mktemp -d "${OPS_DIR:?Set OPS_DIR to your private operations directory}/restore-test.XXXXXX") restic restore latest --tag workstation --target "$restore_dir" ``` I repeat the backup using separately supplied off-site repository settings and verify both copies. I inspect restored files and test an application restore in isolation. I choose a recovery point and recovery time target, then test them. I back up encryption keys through a separate protected process so a machine loss does not make the snapshots unreadable. A successful backup exit alone does not prove recoverability. ## Pin, update, health check and rollback I record the currently working container digest, configuration revision, data version and health check before updating. A Compose image reference uses a registry path followed by `@sha256:` and the full verified digest, rather than a moving tag. I keep the digest in reviewed configuration. Persistent data uses explicit volumes and documented ownership. I update one service at a time. I review release notes, take a consistent backup, pull the selected digest, start the candidate and run readiness plus a real read/write smoke check where appropriate. I compare the measured health with the baseline. A failed stateless candidate is stopped and the previous digest and configuration are restored automatically, followed by the same health checks. An unsuccessful rollback raises an alert and stops the update sequence. Database rollback needs more care: an older image may not understand a migrated schema. Before a stateful update I determine whether migrations are reversible, whether the old version can read the new schema and whether writes must pause. If compatibility is uncertain I test a restored backup separately and use a planned restore procedure with explicit data-loss implications. I never describe changing an image tag as a guaranteed database rollback. I retain the previous digest until the candidate has passed an observation window, and record what changed so the pin remains persistent. ## Retention-based disk cleanup I set separate retention policies for cache content, browser captures, logs and completed job folders. Active jobs, backups, application volumes, credentials, source files and current rollback images are excluded. I keep summaries longer than bulky captures when that meets my audit needs. This dry run lists old regular files only inside the dedicated disposable browser-cache folder. The example age is a policy choice, not a requirement: ```bash cache_root="${OPS_DIR:?Set OPS_DIR to your private operations directory}/disposable/browser-cache" mkdir -p "$cache_root" find "$cache_root" -xdev -type f -mtime +14 -print ``` I review that list, confirm no active job uses the folder, and only then run the corresponding deletion: ```bash cache_root="${OPS_DIR:?Set OPS_DIR to your private operations directory}/disposable/browser-cache" [[ -d $cache_root && ! -L $cache_root ]] || exit 2 find "$cache_root" -xdev -type f -mtime +14 -delete ``` For backups I review a repository-specific restic `forget` dry run before applying a retention policy and prune only after checking available space and a restore. I avoid indiscriminate Docker volume pruning. Cleanup takes a lock against writers, logs counts and reclaimed space without leaking private filenames, and stops if its expected root or ownership check fails. ## Remote access and a human browser policy Cloudflare Tunnel can connect a service through an outbound tunnel without publishing the service directly. I put Cloudflare Access policy in front of remote entry, restrict identities and require suitable authentication. I test denial as well as permitted access and keep the origin reachable only through the intended route. A tunnel alone is not an identity policy. Tunnel credentials stay in protected service configuration. I define what the browser may read, which sites it may visit and when a human must take over. Automation can inspect public pages, measure changes and prepare drafts. A human handles login, CAPTCHA, payment and consent decisions, and approves outward actions. Approval covers the exact action, payload and destination. I never share a person's browser profile among parallel workers. Separate contexts, least privilege and a visible action record keep browser work reviewable. ## My acceptance checklist - A new job has a brief, unique name, isolated files and a resumable checkpoint. - Every dependent job waits for a recorded successful exit, not a guessed delay. - Every planned URL has recorded direct and chain attempts, plus passive rendering when needed. - Blocked content never enters the usable fetch cache, and coverage is explicit. - Alerts report change, failed delivery is retained, and recovery is tested. - User units are validated and enabled, linger is intentional, and timers survive logout. - Notes are searchable with source references, and a model cannot promote its own draft to an approved action. - Backups include local and off-site layers with a tested restore. - Container pins persist, updates are serialized, and rollback respects data compatibility. - Cleanup has a reviewed dry run and never removes active or persistent data. - A shared report passes privacy and secret checks before leaving the machine. - A production change passes every gate, a draft check and live verification. ## October 12, 2026 acceptance additions A shareability gate must report zero hits before release. Apply it to every shared file and every member of an archive, not just the top-level report. For PDFs inspect both extracted text and link annotations; for HTML inspect URL attributes as well as visible text. Keep raw evidence outside the bundle. Distinguish a monitor failure from a site failure before reporting an outage. Check the monitor's own time budget, expected paths and execution environment first. Save progress by stable item identity and source revision, so adding targets to the inventory preserves completed work instead of failing a strict inventory-equality check. Report lost coverage as a change. Use hybrid retrieval for memory: vector similarity helps find related decisions, while keyword search finds exact phrases, rules and identifiers. Validate both against the authoritative source. Test updates in a TEST copy before live promotion, skip the live desktop while someone uses it, and bound retries before large candidate images fill storage. Never run OS package upgrades from a service that can be killed during installation. After any interrupted upgrade, verify package-database consistency, repair incomplete configuration and verify the intended boot image before any reboot. Where no suitable API exists, prepare the human browser handoff completely. For a manual indexing request, check candidate URLs, order, timing and the current published quota rules; use the signed-in browser for the authorized click and record every attempt. A successful URL inspection is not proof of a successful indexing request. Keep secret values out of files, briefs, logs and repositories. Supply them through the process environment from protected credential storage, limit inherited access, and scan before every commit. Verify these requirements before recording acceptance. See the [current operating guide](https://jwatte.com/blog/sovereign-ai-workstation-operating-system/) and its dated sources. These additions are operating recommendations, not new benchmark results. Portable example configuration: choose private operations and user-unit directories and explicitly set any base URL or bind-address variable before using a code example. Bind local services to the numeric loopback interface and keep them private. Code examples are templates to review, not a blind installer. Credential values come from a protected store through the process environment.