# VPS starter contract (Python / Node.js / Go)

All templates implement the same API contract, Linux deployment, no captcha integration. Existing hub endpoint details: `CONTRACT.md`, `docs/BACKUP_CONTRACT.md`.

## Local files / safe first enrollment

Each agent has a locally chosen runtime directory, default `./runtime` (owner-only directory). It writes:
- `config.json`: `{ai_api_key,number_of_workers,ai_preset_id,revision}` (NO captcha key and no hub token).
- `proxies.txt`: canonical URL lines; credentials use percent-encoded `scheme://username:password@host:port`; no credentials in API proxy URL field. Blank/comment lines ignored on import.
- `.sync.json`: `{initialized:true,revision:N}` indicates first enrollment complete, private mode0600.
- `backup-config.json`: latest safe agent backup configuration (`enabled,interval_minutes,destination_ids,agent_configured,agent_last_seen,next_backup_at`), atomically refreshed whenever backup config is polled; cloud credentials never written on VPS. This is a hub-authored runtime snapshot, not a source for importing schedules.

First startup MUST call GET `/api/agent/config` (now adds `revision:integer,local_sync_initialized:boolean`) then, if not locally initialized, POST `/api/agent/local-sync` with `{expected_revision:integer,config?:{ai_api_key?:string,number_of_workers?:integer,ai_preset_id?:string},proxies_content?:string}`. Optional config loaded from localconfig.json (same fields, ignore revision) and optional localproxies.txt; send even emptybodyfields with expectedrevision to finalize enrollment. Server atomically imports local data ONLY when hub configuration is still pristine: no administrator config write and no proxy create/import/delete since sitecreation AND local_sync_initializedfalse. Otherwise it returns current authoritative hub values untouched. Result is full agent config with revision and local_sync_initializedtrue. Once initialized, never reimport localfiles automatically (even on lost local checkpoint, backend guard prevents resurrecting deleted proxies). After first enrollment hub is authoritative; local manual edits are overwritten on next poll. Explicit admin proxy import is available for intentional changes. Race: stale expected_revision returns409; fetch latest then retry once WITHOUT local data, to finalize enrollment without overriding newer admin edits. Reject invalid localfilecontent atomically; log sanitized failure and do not overwrite local files on invalid import.

After successful sync, write config.json and proxies.txt atomically via private temp+fsync+rename, then .sync.json LAST. Readers should read config revision after reload; per-file atomic publication, not a cross-file filesystem transaction. Poll30s; immediately reload on start/restart. Clearing dashboard AI key writes emptystring; deleting proxy writes reduced/emptyfile. Dashboard site deletion revokes token, does NOT delete the codebase or private runtimefiles on VPS (requires explicitlocaloperatoraction). Auth tokens stay environment-only. Files contain secrets: mode0600, directory0700; .gitignore local runtime and cloudconfigs. No file paths or executablecommands accepted from hub.

Local synchronization API:
- POST `/api/agent/local-sync` site token only => full agentconfig `{number_of_workers,ai_preset_id,ai_api_key,captcha_api_key,proxies:[{id,url,username,password}],revision,local_sync_initialized}`. Response NO extra `imported` field required.
- Existing GET `/api/agent/config` adds revision+local_sync_initialized (nonbreaking).
- Existing GET/PUT admin `/sites/{id}/config` adds revision; every administrator PUT config, proxy POST, DELETE, successful non-dryrun proxy import (if inserted>0) marks site admin_modifiedtrue and increments revision. DELETEproxy always revision++, repeated invaliddelete no mutation. First local enrollment sets local_sync_initializedtrue and revision++; next local-sync is idempotent read, not reimport. expected_revision requirednonnegativeinteger. Admin touchedsites ignore local input (but validate schema) and finalize latest hubconfig. Local AIkeyencrypted, proxy passwords encrypted via existingmechanism. Cap1000proxies and100workers. No outbound fetch during localimport.

## Template capabilities

Environment HUB_URL verified HTTPS origin (localhostHTTP devallowed), HUB_AGENT_TOKEN mandatory. Paths chosen locally via env/CLI. Separate heartbeat15s, lifecyclepoll5s, configpoll30s; no blocking backups on lifecycle thread. Worker slots UUID IDs (persistent/random UUID or UUIDv5 supported) with actual CPU/memory if practical; tasks_completed realhookcounter, never fabricated. Harmless placeholder worker loop with TODO businesslogic hook, not an automated target-site interaction. WorkerCount config reconciles runningpool; desired_state recovery onlyinitial, explicit commands start/stop/restart whitelist; persistent completedcommanddedupfile before ack, retryackwithoutrepeating. Explain crashwindownotexactlyonce. Mark commandcompleted onlyafterlocalaction; no remote shell. Report sanitized workererrors POST/errors.

Backup config/status/requestpoll follows backendcontract. Localsource directory configured on VPS only. Periodic off default, interval>=15min, requeston-demand works withperiodicoff; arbitraryfileuploadhelper streams octetstream with filename/source/request_id, no extensionfilter; pack whole codebase tar.gz using localtar command or libraries, no symlinkdereference, temp outsidecodebase and private, exclude own runtime/state/temp if nested. Do not bufferarchiveRAM. Explain stabletree/snapshots and codebaseincludes.env. persistlastuploadtimestamp, boundedsize, cancellationcleanup. Backupthread/task independent, no arbitrarycommandfromdashboard. Failureackonly if not accepted; ambiguouslostresponse reconcile requestqueue, don't falsely claimfailed after hubaccepted. No cloudcredentials required onVPS.

Language ownership:
- Python template under templates/python can reuse `agent/` robust supervisor, backup and new localfilemodule; bootstrap adds repositoryroot tosys.path and instantiates hooks/exampleworker. Must be runnable python main.py, requirements included, usefulautomationextensionpoints. No duplicating robustarchivecode needlessly.
- Node template templates/node runnable node main.mjs, Node22+, minimal/no deps. Real modules inclworker_threadsorchildprocess supervisor, filesystem/proxyformat helper, backuphelper, tests node --test. Localtar argvfixed withoutshell. Streamingfetch needs duplexhalf. HTTPtimeoutandredacterrors.
- Go template templates/go runnable go run . or go build; stdlib preferred, go.mod, real workerpool loopscontext, configfiles/proxyparse, backupgoroutine streamingviafile/tar.gz orlocaltar, httptimeoutsTLS and redirectsdisabled, tests go test ./... . No arbitraryshellstrings.

Documentation must clearly mark integrationhooks vs businesslogic and limitations. Languageparity tests can use a temporary livehub with rclonelocalalias. Root agent must also sync files by default with --runtime-dir (default./runtime), preserving localfiles on firstenroll as above.
