Worker protocol 1
Worker protocol 1
Section titled “Worker protocol 1”This is the process contract for binary programs and the shipped language runners, not an HTTP API. A binary must implement it even if written in Go/C++/Python. Protocol version is literal 1 in its manifest.
Process and channels
Section titled “Process and channels”The platform starts the program as the worker account with a cleared/rebuilt environment, temp working directory and bounded execution time. stdin contains newline JSON request envelopes and RPC replies. fd 3 is the control pipe: every frame is a UTF-8 sentinel immediately followed by JSON and a newline, flushed after writing. Plain stdout/stderr is logging, never protocol. A one-shot process gets one request and exits 0 after its result. --loop means a pooled process: announce READY, serve sequential requests, exit on stdin EOF; it may retain infrastructure state, not previous caller data.
{"request_id":"fixture-1","args":{"name":"Ada"},"ctx":{"project_id":"fixture","request":{},"principal":{},"resources":{}}}ctx includes request/principal/resources/project_id, optional function (library export name), ws and trigger metadata. Request fields and credential caveats are in Context. Preserve the envelope request_id in every outcome.
Frames
Section titled “Frames”| Marker | Body and direction |
|---|---|
___VIAT_READY___ | Bare line, worker→host; only pooled mode, before requests. |
___VIAT_RESULT___ | {request_id,ok: JSON} or {request_id,response:{body_kind,body,status,headers,content_type}} or {request_id,error:{kind,status?,code?,message,extra?}}. Exactly one outcome. |
___VIAT_LOG___ | {level,msg,fields}; debug/info/warn/error structured output. |
___VIAT_STDOUT___ | Verbatim text after marker, not JSON; pooled output attribution. |
___VIAT_PROGRESS___ | {percent?,message?}; detached progress. |
___VIAT_RPC___ | {id,op,name?,req?...} worker→host. |
___VIAT_RPC_REPLY___ | {id,value} or typed error; host→worker on stdin. |
___VIAT_STREAM_BEGIN___ | {request_id,status,headers,content_type}; pooled stream start instead of RESULT. |
___VIAT_CHUNK___ | {request_id,data: JSON}; one item. |
___VIAT_STREAM_END___ | {request_id,outcome:{ok:true}} or {request_id,outcome:{error:{...}}}; note the required outcome wrapper. |
Response body_kind is json, text, file or bytes. File body references {storage,id or key,filename?,disposition?} and is authorized/streamed by the platform; bytes body is {base64,filename?,disposition?} with helper byte limits. Error kind viat preserves intentional status/code/message/extra; runtime/contract/import/protocol errors fail as system execution. Streams require the pooled stream driver; one-shot streaming is a contract error. After stream begin a later failure cannot change HTTP status.
Pooled cancellation arrives as {"request_id":"...","cancel":true}; do not issue a second result for the cancel envelope. A correct long-running implementation needs a reader that can notice it while work is in flight. Ignoring it is not an honest “cancellable” guarantee.
Complete resource-free worker
Section titled “Complete resource-free worker”Save as out/echo-worker, make it executable on your build machine, and use the upload commands on Binary. This deterministic example uses Python only as a portable demonstration of the process protocol, not the Viat Python SDK. It needs Python 3 on the execution node, supports both one-shot and —loop, and does not perform resource calls. It is sequential and does not claim interruption of an in-flight long handler.
#!/usr/bin/env python3import jsonimport osimport sys
control = os.fdopen(3, "w", buffering=1)loop = "--loop" in sys.argv
def frame(marker, body=None): control.write(marker + ("" if body is None else json.dumps(body)) + "\n") control.flush()
if loop: frame("___VIAT_READY___")for line in sys.stdin: envelope = json.loads(line) if envelope.get("cancel"): continue request_id = envelope["request_id"] args = envelope.get("args") or {} if not isinstance(args.get("name"), str): frame("___VIAT_RESULT___", {"request_id": request_id, "error": { "kind": "viat", "status": 400, "code": "invalid_name", "message": "name must be a string", "extra": {}}}) else: frame("___VIAT_RESULT___", {"request_id": request_id, "ok": {"hello": args["name"]}}) if not loop: breakInput above yields a RESULT whose ok is {"hello":"Ada"}; missing/non-string name yields 400 invalid_name. No resource, paid model or message send is involved. Do not run it as an ordinary script without fd 3: it is designed to be launched by the platform.
Resource RPC and security
Section titled “Resource RPC and security”Send ___VIAT_RPC___{"id":1,"op":"http","name":"api","req":{"method":"GET","path":"/health"}} on fd 3. Read ___VIAT_RPC_REPLY___{"id":1,"value":...} from stdin. Failure has error, optionally error_code, status, extra; preserve typed refusals instead of flattening every reply into a generic 500. Match replies by id; pooled stdin can interleave later envelopes and cancellation, so use one reader and queue non-reply envelopes. A program doing concurrent RPC must demultiplex multiple outstanding ids.
Resource names must be declared with # @resource; the broker checks kind, project and operation-specific policy. Prefer broker calls rather than extracting auth fields. Config/secret values can intentionally enter application code; legacy connection-shaped projections differ from broker-only request resources. Never promise that every resource field is credential-free, and never log a projection wholesale.
| Operation | Main request/result contract |
|---|---|
get, set | Declared secret; get returns string/null, set uses frame value, returns null. |
http, graphql | HTTP {method,path,query?,headers?,json? or body_text?/body_b64?} → {status,headers,body}; GraphQL {query,variables?,operation?} → data. |
sql_query, pg_graphql, db_tx | Bound SQL {sql,params?,execute?} → rows/affected; GraphQL document/variables for PG/MySQL/SQLite; transaction begin/query/execute/graphql/set_reason/commit/rollback. |
llm, llm_stream | Messages/options → chat reply; stream replies share id with delta then done:true, not one ordinary value. |
s3, storage | Object/file operations; S3 uses frame fields including s3_op, not the generic req layout. Storage uses tagged req op. |
cloud, crypto | Cloud action/params and crypto tagged operations; platform applies credentials/key material. |
mail_send, imap, push_send, amqp_publish | Send/fetch/message operations; acceptance is not final delivery. |
redis, mongo, library_call | Tagged cache/document operations; library {function,args} returns function output under frozen library build and caller principal. |
authz_check, cache_purge, tasks | {type,action,attrs} → {allowed}; {endpoint} → {purged}; tasks action enqueue/get/cancel. Raw tasks RPC is not a Rust/shell SDK API promise. |
project_users, project_roles, project_read_social_identity, project_provision_meta | Admin operations or narrower current-user grants; not caller-selectable arbitrary user capabilities. |
ws, im, platform_call, social_app_call | Socket/chat/platform operations; resource/account and project gates still apply. |
Per-kind payload options live in the resource references; Context gives guest helper inputs/outputs. Unknown operations and wrong resource kinds fail. There is no blanket RPC retry; avoid replaying writes without idempotency.
Schema, failure and compatibility
Section titled “Schema, failure and compatibility”Rust compile probes --viat-schema for one stdout line ___VIAT_SCHEMA___{"input":...,"output":...}. Binary programs are not probed; do not claim an implemented flag creates an endpoint schema. Exit nonzero, omit a result, malformed frames or invalid result shapes fail the run. Timeout may kill the process without a prior signal; cancellation support is lane-specific. A cancel frame is not durable resume or rollback.
Breaking required framing/envelope changes require a new protocol version; optional fields/operations can be added within version 1. Ignore unknown optional envelope fields while validating required fields. Test both modes on the actual target, then upload/publish deliberately. Keep secrets and provider calls out of conformance fixtures.