Run approved Docker tasks on your own computer from another device without exposing the computer to the Internet or trusting the relay with your data.
CipherJob lets a phone, laptop, or other client ask your workstation to perform a small set of tasks that you chose ahead of time. Your workstation can stay behind NAT with no inbound service open. A public relay moves encrypted messages between the client and worker, but it cannot read the task input or result and it cannot decide what the worker is allowed to execute.
A client does not get a shell. It cannot choose a Docker image, command, mount, secret, environment variable, or resource limit. It can only request capabilities that the worker has installed and authorized locally.
That makes CipherJob useful for things such as running a build from your phone, invoking a private local model, processing a file on a powerful workstation, triggering a trusted automation, or eventually running a coding agent without turning the machine into a general remote execution server.
CipherJob 0.3.0 is the Phase 3 release.
Most remote execution systems solve reachability by giving the remote side broad access to a machine or by putting a trusted service in the middle.
CipherJob uses a narrower model.
phone / laptop
|
| signed + encrypted request
v
+-----------------------+
| public blind relay |
| |
| routes ciphertext |
| cannot read the job |
| cannot authorize it |
+-----------------------+
|
| opaque ciphertext
v
+-----------------------+
| your worker |
| |
| verifies device |
| checks local policy |
| maps capability ID |
| to local Docker task |
+-----------------------+
|
v
restricted container
|
| signed + encrypted result
v
client
The central rule is simple:
The relay controls delivery. The worker controls execution.
The relay can make a message late, unavailable, or duplicated. It cannot turn a request into a different capability, authorize a new device, select a different image, or read the encrypted payload.
CipherJob separates network reachability from execution authority.
| Property | CipherJob |
|---|---|
| Public worker port required | No |
| Remote shell | No |
| Client chooses Docker image | No |
| Client chooses command | No |
| Relay sees task input | No |
| Relay sees task output | No |
| Relay can authorize execution | No |
| Worker owns network policy | Yes |
| Worker owns workspace policy | Yes |
| Worker owns resource limits | Yes |
| Tasks run in containers | Yes |
| Structured JSON jobs | Yes |
| Encrypted progress events | Yes |
| Resumable encrypted artifacts | Yes |
The worker exposes a signed capability menu instead of a shell or arbitrary Docker API. Each capability maps to trusted local configuration.
This keeps the wire protocol small while still allowing very different applications to run on top of it.
CipherJob 0.3.0 provides a complete path from device enrollment to reliable container execution.
| Area | Phase 3 behavior |
|---|---|
| Enrollment | Approval-based device enrollment with a verification code |
| Authentication | Passkey-protected public client and worker-side device authorization |
| Scheduling | Leases, retries, cancellation, backpressure, and bounded concurrency |
| Requests | String and structured JSON inputs |
| Results | String and structured JSON outputs |
| Progress | Ordered end-to-end encrypted job events |
| Artifacts | Resumable encrypted file transfer |
| Tasks | Container-native multi-action task images |
| Isolation | Worker-controlled Docker sandbox policy |
| Users | Stable local users independent of individual device keys |
| Devices | Multiple authorized devices with revocation |
| Workspaces | Persistent per-user worker workspaces |
| Operations | Local operator dashboard, doctor checks, pause, drain, and resume |
| Relay | Cloudflare Worker and Durable Object control plane |
The cryptographic wire protocol is v2, the leased relay API is v3, and container task images use task protocol v1. Those are independent version numbers for separate layers of the system.
Requirements:
- Python 3.11 or newer
- Docker
- Node.js and npm
- a Cloudflare account for the public relay
Clone the repository:
git clone https://github.com/Aryadeepta/cipherjob.git
cd cipherjobSet up the local environment:
./scripts/quickstart.shCheck the worker:
cipherjob doctor --config .cipherjob/worker.yamlOpen the local operator dashboard:
cipherjob operatorDeploy the public relay:
cipherjob deploy cloudflareCreate a join link and terminal QR code:
cipherjob share --qrOpen the link on another device, create or unlock the passkey, request access, compare the verification code, and approve the device from the operator dashboard.
The browser can then load the authenticated capability menu and run installed tasks.
See docs/GETTING_STARTED.md for the full setup flow.
CipherJob does not expose Docker itself to the network.
A remote job contains a capability ID and input. The worker resolves that capability ID to local configuration. The local configuration determines the image and all execution policy.
A typical container starts with restrictions equivalent to:
docker run
--rm
--interactive
--pull=never
--read-only
--cap-drop=ALL
--security-opt=no-new-privileges=true
--network=none
--memory=<worker policy>
--cpus=<worker policy>
--pids-limit=<worker policy>
--tmpfs=/tmp:...
<immutable image id>
Capabilities may opt into a worker-controlled read-only or read-write workspace. Network access is denied by default. Host environment variables are available only when explicitly configured by the operator.
The remote client cannot weaken these settings.
Task images are installed by immutable Docker image ID so a mutable local tag cannot silently change the image referenced by worker configuration.
cipherjob doctor checks that configured task images still exist locally before the worker accepts jobs.
A task image can expose several related actions from one OCI image.
For example:
{
"version": 1,
"id": "task-toolbox",
"label": "Task Toolbox",
"description": "Small example task image.",
"actions": [
{
"id": "echo",
"label": "Echo",
"description": "Return the input unchanged.",
"risk": "green"
},
{
"id": "sha256",
"label": "SHA-256",
"description": "Hash the input.",
"risk": "green"
}
]
}Install it with:
cipherjob task install examples/task-toolbox \
--config .cipherjob/worker.yamlThe image describes its actions. It does not grant itself authority.
The worker still decides:
network access
workspace access
resource limits
timeouts
host environment
authorization
concurrency
This boundary is important. A future coding agent can be packaged as a task image without adding special agent privileges to the relay or core protocol.
See docs/TASK_IMAGES.md.
Capabilities can accept either strings or JSON values.
A future coding task could receive an input such as:
{
"prompt": "Add a streaming parser",
"workspace_artifact": "art_123",
"mode": "edit",
"options": {
"run_tests": true
}
}String capabilities remain supported.
Long-running jobs can emit ordered progress and log events while they execute. Those events use the same end-to-end encrypted reply path as other private job data. The relay routes ciphertext and does not receive plaintext logs.
The final result remains a separately authenticated result message.
Large inputs and outputs do not need to fit inside one job message.
CipherJob provides resumable encrypted artifacts with chunk hashing and worker-local storage.
Example:
cipherjob artifact put ./input.tar --client-config .demo/client.yaml
cipherjob artifact list --client-config .demo/client.yaml
cipherjob artifact get art_... --output ./result.tar --client-config .demo/client.yaml
cipherjob artifact delete art_... --client-config .demo/client.yamlArtifacts are scoped to a stable local user. Multiple authorized devices for that user can access the same workspace without sharing a device private key.
The relay sees routing metadata, timing, and ciphertext sizes. It does not receive the artifact contents in plaintext.
Remote execution needs stronger semantics than a simple queue.
Phase 3 adds leased delivery so a worker crash or lost response does not silently discard queued work. The worker renews active leases, applies bounded concurrency, handles cancellation, backs off when work cannot be accepted, and suppresses automatic replay of an execution that may already have caused side effects.
Jobs have persistent local state so worker recovery can distinguish a completed request from an interrupted one.
This matters for real workloads. A build, deployment, file mutation, or agent action cannot be treated like an idempotent text lookup by default.
CipherJob uses two independent authorization layers.
The public service authenticates access to relay resources. The worker separately authenticates the device that signed the encrypted request.
Compromising relay authentication is therefore different from compromising execution authorization.
Protocol v2 uses:
Ed25519 signatures
RFC 9180 HPKE Base mode
DHKEM(X25519, HKDF-SHA256)
HKDF-SHA256
ChaCha20-Poly1305
Signed payloads use domain separation. Broker-visible routing metadata is authenticated as associated data. Executable request IDs are checked against worker-local replay state.
The worker does not trust capability metadata from the client. The client cannot request an arbitrary image, command, mount, network mode, or secret.
The relay still observes traffic metadata such as route identifiers, request timing, expiration, ciphertext sizes, and network addresses. End-to-end encryption does not hide that metadata.
For the detailed threat model, see SECURITY.md, docs/THREAT_MODEL.md, and docs/PROTOCOL_V2.md.
The worker has a local management interface:
cipherjob operatorIt binds to localhost and provides the operational controls needed for one worker:
worker status
doctor results
public relay status
installed tasks
task image health
join link and QR code
pending enrollment approval
authorized device management
pause
drain
resume
State-changing requests use a per-launch management token.
The operator UI is deliberately local. CipherJob does not add a second remote administration service beside the encrypted job protocol.
I built CipherJob to exercise the parts of software engineering that are easy to hide in a small demo.
The system crosses Python, TypeScript, React, Cloudflare Workers, Durable Objects, WebAuthn, public-key cryptography, SQLite, Docker, Linux resource controls, HTTP, WebSockets, and local operator tooling. Those pieces have to agree on protocol semantics and failure behavior.
The engineering work is visible in the boundaries:
| Engineering area | What this project demonstrates |
|---|---|
| System design | Clear separation between routing, authorization, execution, and application tasks |
| Security | Explicit trust boundaries instead of relying on network location |
| Distributed systems | Leases, retry behavior, cancellation, backpressure, deduplication, and recovery |
| Containers | Read-only execution, dropped capabilities, resource ceilings, network policy, workspaces, and immutable image references |
| Protocol design | Signed canonical payloads, encrypted envelopes, versioned schemas, structured jobs, and ordered events |
| Product engineering | Passkey login, QR enrollment, operator approval, device management, and useful diagnostics |
| Debugging | End-to-end failures were traced through Cloudflare storage semantics, relay behavior, Docker image identity, and worker state |
| Compatibility | Existing string tasks continue to work while the data plane gained JSON, artifacts, and progress |
| Testing | Python, relay TypeScript, browser TypeScript, production web builds, and live phone-to-Docker execution are all part of release validation |
| Scope control | Agent behavior stays outside the core protocol so future applications can be added as capabilities instead of privileged platform features |
The part I value most is that the project has been tested as a real system rather than only as isolated modules. Bugs found in live use changed the design. Duplicate task installation exposed a mutable-tag failure mode. Cloudflare SQL write accounting exposed an incorrect lease assumption. Operator testing exposed UX and diagnostics problems. Those were fixed at the layer that owned the problem and covered with regression tests.
That is the kind of engineering I want this repository to represent: understanding a system across layers, finding the actual failure boundary, and leaving the architecture cleaner after the fix.
CipherJob core does not need to know what a coding agent is.
A coding agent can be another installed capability that receives structured input, accesses an authorized workspace, emits progress, transfers artifacts, and returns a structured result.
That is the Phase 4 direction.
The transport, authorization, scheduling, container isolation, artifact channel, and operator controls remain general infrastructure.
CipherJob 0.3.0 is an educational and portfolio security project. It has not received an independent security audit and should not be treated as a hardened hostile multi-tenant execution service.
The intended deployment is a self-hosted worker controlled by its operator with a narrow set of explicitly trusted task images.
| Path | Purpose |
|---|---|
src/cipherjob/ |
Python client, worker, protocol, scheduler, task runtime, artifacts, and operator tooling |
relay/ |
Cloudflare Worker and Durable Object relay |
web/ |
Passkey-protected browser client |
examples/task-toolbox/ |
Multi-action task image example |
docs/GETTING_STARTED.md |
Current setup guide |
docs/TASK_IMAGES.md |
Task image contract |
docs/PROTOCOL_V2.md |
Cryptographic wire protocol |
docs/THREAT_MODEL.md |
Security boundaries and assumptions |
SECURITY.md |
Security status and reporting |
python3 -m pytest -q
python3 -m ruff check .
cd relay
npm run check
cd ..
cd web
npm run check
npm run build
cd ..cipherjob versionExpected output:
CipherJob 0.3.0 | platform phase 3 | crypto protocol v2 | relay API v3 | task protocol v1
CipherJob is licensed under the Apache License, Version 2.0.
See LICENSE, NOTICE, THIRD_PARTY_NOTICES.md, and docs/OPEN_SOURCE.md.