NAME — tiny · credential-less Claude Code & Codex sessions on your own Kubernetes
An agent that reads issues and pulls dependencies will eventually be told what to do by someone who is not you. tiny runs the real Claude Code and Codex CLIs as pods on your own Kubernetes, holding nothing worth stealing: no git credentials, no cloud credentials, nothing listening on the pod, egress denied by default. Work leaves as a signed-off bundle a courier pushes. When it asks to do something risky, your answer runs as you — not as the agent. And the session keeps working after you close the lid.
The session lives in the cluster, not on your machine. Mid-conversation locally? tiny handoff moves it there, uncommitted changes and transcript included. Close the lid; attach from any terminal later.
tiny runs the real CLI, so you sign in the way you do on your laptop: Claude Pro/Max or ChatGPT Plus, the same plan you already use. (Automated or customer-facing work should use a metered API key under the provider's terms.)
When a session hits your plan's usage limit, its fleet row shows ⏸ resuming 5:20am, and it resumes then on its own.
The fleet screen shows who is waiting on you. You can answer inline, message a session without attaching, or drop a file onto its row.
Repo credentials. The agent has none. Work leaves as a git bundle; a courier pushes it with a short-lived token the agent never sees. It cannot push, force-push or reach your other repos — the capability is absent, not restricted.
Cloud credentials. None in the pod, and 169.254.169.254 — the endpoint that hands out an instance’s IAM role — is blocked.
Inbound network. Nothing listens. No container port; the MCP sidecar binds 127.0.0.1. You reach a session through the Kubernetes API under your own RBAC, never through an open port.
Acting as the agent. Approving a question runs in your client with your credentials. That is why the web page can only read.
The model credential lives in the agent pod. Running the real vendor CLI requires it. Inherent, not an oversight.
HTTPS out, unless you narrow it. A NetworkPolicy matches addresses, not hostnames, so by default any host on 443 is reachable. Switch on the hostname allow-list and only named hosts are — though allow-listing github.com still means an agent can write to a gist.
The agent’s own tool calls. It runs with bypassPermissions. Asking is cooperative — nothing intercepts it. The boundary is the pod, the missing keys and the policy.
Detail: threat model · egress policy · the gate
.mcp.json. tiny does not wrap or proxy the agent, so new agent features work without us doing anything.golang:1.26, buildah, your own dev image: an init container injects the agent into whatever you name. You do not maintain a special image./workspace/uploads/.--agent codex runs OpenAI’s Codex. The fleet screen, the gate and resume behave the same for both.claude --model / codex -m underneath). Also a field on the options form.[b] on the fleet screen, or tiny broadcast from cron and CI.Pull-through image cache in your namespace. golang:1.26 downloads once instead of once per session, which also keeps you under docker.io rate limits. An optional DaemonSet installs its CA on the nodes.
S3-compatible store next to the sessions, for things too big for git: builds, screenshots, datasets.
In-cluster Actions runner, so delivery jobs can reach the sessions. A labeled issue lands in an inbox in about five seconds.
The fleet as a page, plus each session’s blast radius: files changed, lines, branch, and the files two sessions are both editing. Port-forward to reach it; it can only read.
Default-deny networking for sessions: this namespace and http/https out, nothing else. Closes the cloud metadata endpoint and every other port. It does not stop exfiltration over 443 — and it only bites if your CNI enforces NetworkPolicy, which the screen tells you.
All outbound traffic through a CONNECT proxy that filters by name, not address — the thing a NetworkPolicy cannot do. TLS is never terminated; the hostname is read before the tunnel opens. Closes the DNS channel too. Refusals name the host and get logged.
Recorded against a real cluster, including the kubectl delete pod. Tapes in the repo.
File a GitHub issue and add the tiny label. That is the whole ceremony. Anyone on the team can do it from the GitHub UI.
An Actions job on the in-cluster runner pipes the issue into the root session's inbox. It usually takes about five seconds.
The root session plans, spawns specialists in the right toolchains, and raises a ✳ when a decision needs a human. Overnight is fine; we test session recovery by killing pods mid-task.
Agents hold no credentials. Finished branches leave as git bundles through the outbox, and a short-lived courier token opens the pull request and comments on your issue.
PROOF — this loop produced a real pull request in our demo repo: github.com/tiny-systems/seedling
A coding agent reads untrusted text for a living. Ours runs with permissions bypassed and holds no credentials at all. The reasoning, and the part we haven't solved.
The second agent took an afternoon. The two bugs it surfaced were nowhere near the model.
Job tokens die with their jobs and deploy keys sprawl, so our agents stopped pushing entirely.
One binary, one wizard, no pods until your first session.
$ brew install tiny-systems/tap/tiny $ tiny setup # cluster · runtime · claude token $ tiny new "make the tests not lie"
tiny pins one cluster per machine, chosen with an arrow-key picker on every start — enter‑enter repeats yesterday's choice. Read the full manual →
tiny is MIT-licensed. We are early, and stars are the main way people find small projects like this one — if you want this to keep growing, a star genuinely helps.