How the pieces connect
In the last post I showed how a message on my phone ends up live on bitsmonk.com. This one looks under the hood: what connects to what, over which protocol, and who holds which key.
Here's every hop, top to bottom:
- My phoneClaude app
- HTTPS (TLS)Signed-in Claude accountConnection opened by the previous item.
- Anthropic APIRoutes messages, keeps the transcript in sync
- HTTPS, outbound onlyServer polls for work; short-lived scoped credentialsConnection opened by the next item.
- AWS EC2Claude Code in Remote Control server mode
- git over HTTPSFine-grained token, one repositoryConnection opened by the previous item.
- GitHubRepository and Actions CI
- GitHub App eventCloudflare Workers and Pages appConnection opened by the previous item.
- CloudflarePages build and global network
- HTTPSDNS and TLS on CloudflareConnection opened by the next item.
- VisitorAny browser
The arrows show something that's easy to miss. Two of the connections run "backwards": the server reaches out to Anthropic, and visitors reach out to Cloudflare. Nothing ever connects into my server.
1. Phone → Anthropic → AWS
My phone never talks to the server directly. They meet in the middle, at the Anthropic API.
Claude Code runs on an EC2 instance in Remote Control server mode. When it starts, it registers with the Anthropic API and then keeps asking, over HTTPS, whether there's any work for it. When I open the session in the Claude app, Anthropic routes my messages to that waiting process and streams its replies back to my phone.
A few details that matter:
- No open ports. In the words of the Claude Code docs, the session "makes outbound HTTPS requests only and never opens inbound ports on your machine." The server needs no inbound firewall rule for any of this.
- Encrypted, with narrow keys. Everything travels over TLS, using several short-lived credentials, each scoped to one job.
- The work stays on the server. The conversation is synced through Anthropic so it shows up on every device. The code, the files and every command stay on the EC2 instance.
- One workspace per session. In server mode, each session can get its own git worktree, so two conversations never edit the same files.
2. Inside the server
Claude works on its own once I've asked for something, so what it can't touch matters as much as what it can.
AWS EC2 instance
Ubuntu, always on
Claude Code
Remote Control server, signed in to my account
Session worktree
Its own git worktree of the repository
Command sandbox
Every shell command runs in here
- Write
- Only the project folder and a temp folder
- Read
- Most files, but never credential stores
- Network
- Only hosts the command declares, through a filtering proxy
Every shell command runs inside an operating-system sandbox. On Linux that's bubblewrap, the same tool Flatpak uses to isolate apps. The rules are simple:
- Writes go only to the project folder and a temp folder.
- Reads cover most of the system, except credential stores like
~/.git-credentials. - Network traffic goes through a filtering proxy. Each command declares the hosts it needs, such as the npm registry, GitHub or Google Fonts, and everything else is refused.
That's also why pushing to GitHub needed one more step.
3. AWS → GitHub
Pushing is plain git over HTTPS to github.com. The sandbox blocks the server's own git credentials, so the push uses a key I created just for this: a fine-grained personal access token.
- It can access one repository and nothing else on my account.
- It can write Contents (commits and branches), Pull requests and Workflows, and nothing account-wide.
- It lives in a git-ignored file in the project folder. Git only reads it for the moment of each push:
git -c credential.helper= \
-c credential.helper='!f() { echo username=me; printf "password=%s\n" "$(cat .push-token)"; }; f' \
push origin HEAD:mainThe empty credential.helper= clears any saved helpers, and the second one hands git the token for this single command. Nothing gets written to git's config or the remote URL.
The first push failed with "refusing to allow a Personal Access Token to create or update workflow without workflow scope." The commit included the CI file in .github/workflows/, and GitHub treats changes to automation as more sensitive than changes to code. Adding the Workflows permission to the token fixed it.
4. GitHub → Cloudflare
Cloudflare's GitHub App, Cloudflare Workers and Pages, is installed on the repository, and only that repository. When a push lands on main:
- GitHub notifies the app about the new commit.
- Cloudflare clones that commit into a build container, reads
.node-versionto choose Node.js, runsnpm run buildand collects theout/folder. - It uploads the files to its network and makes the new deployment live. Pushes to other branches get their own preview URLs instead.
- The result appears on the commit in GitHub as a check run.
At the same time, GitHub Actions runs its own CI on the same commit: linting, type checks, the build and the browser tests.
These two run in parallel, and Cloudflare doesn't wait for CI. The real gate is the full test suite Claude runs on the server before it pushes. CI is a second opinion that can flag a problem, but it can't stop a deploy that's already happening.
5. Cloudflare → you
bitsmonk.com is a Cloudflare zone, which Pages requires for a root domain. When you visit:
- DNS answers with Cloudflare's addresses, so you reach a nearby data center.
- That data center handles TLS and serves the static files straight from Cloudflare's network. There's no origin server behind it.
- A
_headersfile in the build adds security headers (a Content Security Policy, HSTS) and long-lived caching for files whose names change with every build.
One change, end to end
Phone
I send a message in the Claude app.
Anthropic
Routes it to the session waiting on my server.
AWS
Claude edits the code, builds the site and runs the tests inside the sandbox.
AWS → GitHub
The change is committed and pushed to main with the fine-grained token.
GitHub
Notifies Cloudflare and starts CI on the same commit.
Cloudflare
Builds the site, uploads it and switches bitsmonk.com to the new version.
Phone
Claude reports back what changed and that it's pushed.
What I'd tighten next
- Deploy only after CI passes. Turn off Cloudflare's automatic builds and deploy from GitHub Actions once the tests are green, so CI becomes a real gate.
- Replace the token. Linking GitHub directly to my Claude account would let Claude push through a GitHub App instead, with no long-lived token on disk.
- Set expiry dates. Until then, the token gets an expiry date and a calendar reminder.
None of this is exotic. It's HTTPS, git, a sandbox and a GitHub App. What makes it work is that each piece only gets the access it needs.