Declare your Macs. Kubernetes keeps them.
The carafe Fleet operator runs Fleet, the open-source device manager from FleetDM, inside your cluster. Through Fleet it checks that every Mac station is awake, logged in and running its AI agent (Claude Code, Codex or your own), and runs the configured fix when a check fails.
lab-tested, not yet in production Measured 2026-10-28 in a kind cluster against a macOS 26.5 VM, checks every minute.
NAME ONLINE CELLAR-DOOR CHROME SIGNED-IN STATUSstation-01 true true true true ok# 0 s the agent's page is stopped on the Macstation-01 true false true true attention# 51 s policy failing → remedy restart-cellar-door.sh# script exit 0, output "ok"; host refetchedstation-01 true true true true ok# 142 s back, nobody at the screen
Five layers of AI. carafe is layer 3, the infrastructure.
Jensen Huang describes AI as a five-layer stack: energy, chips, infrastructure, models and applications. Infrastructure includes power delivery, networking and the systems that run many processors as one machine. carafe is building that layer for AI agents: a Mac mini cloud that keeps each agent's machine powered, connected and running.
Framework from Jensen Huang, "AI Is a 5-Layer Cake", NVIDIA blog, 2026-03-10. Placing carafe on it is our reading; carafe is not affiliated with or endorsed by NVIDIA.
An AI colleague only works while its computer is on.
The agent stops.
Claude Code, Codex and other agents run on someone's laptop. Remote access helps, but only while that laptop is awake. Not everyone can keep a computer on, or set one up.
A container has no browser.
Permit applications on government portals and design proposals happen in web pages. Claude in Chrome drives a real Chrome on a real desktop, so the plan is a Mac mini for each of them.
Someone has to keep them alive.
Every station must stay awake, logged in, with the agent and its browser running, and be fixed from afar when it is not. Doing that by hand in a device console adds one more place for settings to drift.
One always-on Claude workspace per colleague, reachable from the phone.
A carafe is one person's own container with Claude Code, their work folder and the company's skills. A Mac station runs the same setup natively on a Mac mini, with a real Chrome. Each has one web page, the cellar door, where the person signs in and turns on phone access. Then they work from the Claude app.
- A session started from claude.ai/code, the same path the phone uses, drove Chrome on a Mac station. After a reboot the station came back by itself in about two minutes. A test on a real phone is still to come.
- The Fleet operator reports every station's checks and runs the configured fix when one fails.
The sign-in happens in the person's own browser, through Anthropic's flow, and the code goes straight into their own carafe. carafe keeps no copy of anyone's Claude credentials.
"Trust this folder?" and "Enable Remote Control?" are shown verbatim and answered by the person. carafe never answers them for anyone.
The signed-in check has no remedy. Signing in again needs the person, so the operator reports it and stops there.
One operator. Three resources. One writer.
The operator owns Fleet: it deploys it, sets it up, configures it and upgrades it. Fleet in turn manages the Macs. The operator is meant to be the only thing that writes to Fleet.
FleetServerone per companyFleet, MySQL and Valkey from the official Helm chart: spec.version picks Fleet, spec.chartVersion the chart. Then TLS, the first admin, an API user, Secrets, NodePort and NetworkPolicies.
FleetConfigone per companyPolicies, scripts and labels, kept matching the spec: removed from the spec means removed from Fleet. Built-in and person labels are left alone.
MacStationone per personAn enrolled Mac, found by serial number: labelled with its person, every check in status, and the remedy script run when a check fails.
A health check and its fix, declared in YAML.
Each policy is an osquery check that runs on every Mac. Give it a remedy and the operator runs that script when the check fails and the Mac is online. Give it none and the operator only reports.
- Five station checks ship in the example: never sleeps, logs in by itself, the agent page answers, Chrome is running, Claude is signed in.
kubectl get macstationsshows every person's Mac, with the five station checks as columns.- Hand edits are reverted at the next drift check.
kind: FleetConfig spec: serverRef: fleet policies: - key: cellar-door platform: darwin query: SELECT 1 FROM curl WHERE url = 'http://127.0.0.1:7681/healthz' AND response_code = 200; remedy: restart-cellar-door.sh # run when it fails - key: signed-in query: SELECT 1 FROM curl WHERE url = 'http://127.0.0.1:7681/signedin' AND result = 'yes'; # no remedy: signing in again is the person's job --- kind: MacStation spec: serverRef: fleet person: ana serial: C02ABCDEF123 remedyBackoff: 120 # seconds between fixes
Claude Code, Codex or your own agent.
If it runs on macOS, the operator can watch it. A check is an osquery query, a fix is a shell script run as root, and both live in your spec. Anything osquery can see counts: a process, a launchd job, a port, a heartbeat file.
| agent on the station | what to check | status |
|---|---|---|
| Claude Code, in a carafe Mac station | agent page answers, Chrome running, Claude signed in | lab-tested |
| OpenAI Codex CLI | process running, its LaunchAgent loaded | same pattern |
| your own agent or bot | a process, a port, a heartbeat file | same pattern |
Lab-tested: carafe's checks and its agent page's recovery. Codex and custom agents are examples, not yet tested; their checks show in status.checks.
kind: FleetConfig spec: serverRef: fleet scripts: - name: restart-codex.sh content: | #!/bin/sh # your own LaunchAgent that runs Codex l=com.example.codex u=$(stat -f %Su /dev/console); uid=$(id -u "$u") launchctl kickstart -k "gui/$uid/$l" || launchctl bootstrap "gui/$uid" \ "/Users/$u/Library/LaunchAgents/$l.plist" policies: - name: "station: Codex is running" key: codex platform: darwin query: SELECT 1 FROM processes WHERE name = 'codex'; remedy: restart-codex.sh # run when it fails
The spec is the only truth. Hand edits are put back.
No edits in Fleet's web page, no separate GitOps run, no tug of war between two tools. The operator reads Fleet back every five minutes and makes it match.
| what happened | what the operator did | measured |
|---|---|---|
| a policy edited in the spec | applied to Fleet | under 1 s |
| a policy added by hand in Fleet | removed | at the next check |
| a script changed by hand in Fleet | put back | 287 s |
A failed check triggers its fix, carefully.
The operator runs a remedy only when four things hold. That keeps a broken script from hammering a Mac, and skips Macs that are off.
- The policy is failing, per Fleet's own result.
- The spec names a remedy for it.
- The Mac is online. Nothing new is sent while Fleet reports it offline.
- The backoff has passed since the last run.
After a reboot every check came back with nobody touching the Mac. The operator works out boot time from uptime, because in the lab a quick reboot never showed as offline in Fleet; it landed within one second of the kernel's own boot time.
Runs on Fleet's free tier.
Fleet's own "run a script when a policy fails" is a Premium feature. The operator does it itself with what the free tier already has: scripts, policies, labels and an API-only user.
When we looked in October 2026 we found no Kubernetes operator for Fleet, from its maintainers or the community. The options were the Helm chart, a Terraform provider and fleetctl gitops. The operator uses the official chart rather than replacing it.
Fleet tiers per fleetdm.com/pricing, read 2026-10-27.
| capability | Fleet Free | with the operator |
|---|---|---|
| policies, scripts, labels, API-only users | yes | declared in YAML |
| run a fix when a policy fails | Premium | yes with backoff |
| install, set up, upgrade Fleet | separate tools: Helm, Terraform | yes from spec.version |
| keep Fleet matching a spec | when fleetctl gitops runs | yes every 5 min |
| several companies apart | one org per install | yes one per namespace |
The token that can run root on every Mac stays in one namespace.
- Least privilege in Fleet. The operator's API user defaults to maintainer, the lowest role that can run scripts, not admin.
- Least privilege in Kubernetes. A namespaced Role, never a ClusterRole. The operator watches one namespace only.
- NetworkPolicies by default. MySQL and Valkey only from inside the namespace; from outside the cluster, only Fleet's port.
- The AI agents' pods cannot reach Fleet. The lab found that a pod going to a node's NodePort keeps its pod address, so the rule for the Macs excludes the pod network. In the kind lab, pods in other namespaces timed out on the Service, the NodePort and MySQL. Nothing else runs in Fleet's namespace.
| # | company A against company B | result |
|---|---|---|
| 1 | second Fleet comes up beside the first | 71 s first untouched |
| 2 | each sees only its own hosts | apart |
| 3 | one company's API token on the other's Fleet | 401 both ways |
| 4 | one company's enroll secret on the other's Fleet | 401 both ways |
| 5 | one company's CA on the other's Fleet | TLS refused |
| 6 | a pod to the other's Fleet, MySQL, Valkey, NodePort | blocked both ways |
| 7 | one operator's account on the other's namespace | denied both ways |
| 8 | A's Mac declared as a station of B | not enrolled nothing shown |
One Fleet per company, tested apart.
Each company gets its own namespace, operator, Fleet, database and port, and later its own Apple push certificate. Fleet's free tier has one organization per install, so this is also how each company sees only its own name and its own Macs.
Two companies side by side in the kind lab, 2026-10-28; the first had one Mac enrolled, the second none.
Built twice, measured twice.
A Python proof of concept first, then a Go rewrite with kubebuilder and controller-runtime that runs inside the cluster, calling Helm's Go SDK in-process. Both passed the same six checks against a real macOS VM. The Python code was then removed.
| # | check | Go operator, in the cluster | Python POC |
|---|---|---|---|
| A | empty namespace to a working Fleet, users and Secrets | pass Ready in 71 s | 97 s |
| B | spec applied; hand edits undone | pass < 1 s; 287 s | 2 s; 71 s |
| C | a Mac enrolls and shows its checks | pass listed after 10 s | pass |
| D | the agent page stopped, fixed by the operator | pass 51 s / 142 s | 82 s / 171 s |
| E | reboot, every check back untouched | pass boot time ±1 s | pass |
| F | other namespaces cannot reach Fleet | pass timeouts on all paths | pass |
Fleet 4.93.2, Helm chart 7.0.30, kind 0.33 with Kubernetes 1.37, macOS 26.5 VM. Lab checks ran every minute; about every five minutes is proposed for stations. Single runs, one macOS VM.
Proven in the lab. Next: MDM and a first real office.
Nothing here runs in production yet. Lab-tested; MDM, backups and a first office come next.
Proven lab
- Go operator: install, configure, drift, remedy, reboot, isolation (A–F)
- Two companies side by side, eight isolation checks
- Unit tests against a fake Fleet and a fake API server, no cluster needed
- Trusting the company CA on a Mac as root, with nobody at the screen
Next planned
- MDM: Apple push certificate in a Secret, expiry in
status, enrollment profile from the API - First profile: Chrome's managed policy that installs Claude in Chrome
- Nightly MySQL dump, with a restore tested before stations depend on it
- The NetworkPolicy checks again on k3s; a per-company install overlay
Later open
- Zero-touch enrollment through Apple Business Manager
- Several fleets per install with Fleet Premium
- OS updates, lock and wipe through MDM
- A company-owned carafe box for small offices, with an engineer for setup and workflows
Next: an agent-ready office for a ten-person company.
A small company has no IT team and no Kubernetes. The plan: it buys a carafe box that becomes its control plane, with no data centre to rent and nothing hosted by us. An engineer sets it up, then works on the team's workflows.
carafe boxthe control planeA Raspberry Pi 5 running k3s and the operator. The company owns it and decides who is admin; support would get in only with its approval, each visit logged.
carafeone per personClaude Code, a work folder and the company's skills, on a host in the office, reached from the phone.
Mac stationone per boundaryA Mac mini for each role that must stay apart: front desk, finance, back office, audit, the owner. Each checked and fixed by the operator through Fleet.
networkthe office's ownClaude Code's Remote Control opens no inbound ports, so phones need no public address; Fleet stays on the office LAN. A recommended UniFi kit would add a VLAN for the agents, PoE and a 5G backup line.
The box goes in.
An engineer, ours or a partner's, installs the box, sets up the network and sign-in, and gives everyone a carafe.
Work, not plumbing.
The setup is the same in every office, so the engineer's time goes to one or two workflows for each team.
Skills travel.
Those workflows become skills that the next company of the same kind can start with. A subscription per person would cover monitoring, upgrades and skill updates.
Source available. Free for noncommercial use. Licensed for companies.
For IT teams that already run Kubernetes, for service providers who look after Macs for several small companies, for consultancies placing AI Mac stations with their clients, and, next, for small companies and the engineers who set them up.
Write for a commercial license or a scoped pilot on your own Mac stations.
Independent software. Not affiliated with or endorsed by Fleet Device Management, Anthropic, OpenAI, NVIDIA, Apple, Raspberry Pi or Ubiquiti.
Noncommercial
Free for the uses the PolyForm Noncommercial License 1.0.0 permits.
Commercial
Commercial use, including running carafes for an organization's own staff or its clients, needs a separate written license signed by Cheng Cheng.
Yours stays yours
The licensor claims nothing in what a person keeps or makes in their carafe: workspace files, sessions, transcripts, the configuration carafe writes.
Third parties
Fleet, Claude Code, Codex and macOS keep their own terms. carafe's license covers carafe's own code only.