Working Note 004
Nothing built, and the design lost a server
I set out to bridge Signal into a Cloudflare pipeline and spent the session reading documentation instead. Capture and collaboration turned out to be two jobs, and one channel was buying both — which is where the phone number, the JVM and the second always-on host came from. Nothing was built, so the finding is documentation and arithmetic.
Finding
Capture and collaboration are two jobs, and asking one channel to do both is what made Signal look necessary. Capture wants to cost nothing — five seconds on a pavement, no app to open, no decision about where the thought lands. Collaboration wants a repository, a voice document and a conversation with something that has read them. Signal was in the plan because it is where I would want the second of those, and it dragged its price into the first.
That is a design conclusion, not a result. Nothing below has been built: no Worker, no capture path, no note that reached this site by any route other than me typing it. The strongest evidence in this note is a price list, and conclusions reached before building are usually wrong about the parts that building would have shown.
Why I looked at this
Not as a platform review. Notes start away from the desk. On a walk, on a train, in the four minutes before something else — and by the time there is a keyboard, what survives is a summary of the thought rather than the thought.
Signal was in the plan for one reason, and the reason never got examined: it was already on the phone. That is the whole of its qualification. It went unchallenged until I priced it.
The question
Does one messaging channel carry a thought from a pavement to a published page, or is it two jobs sharing an icon — and if it is the second, which job pays for the other?
What I expected
That the agent was the hard part and the transport was plumbing. A bot, a webhook, a weekend. I also assumed publishing would need work, on the theory that anything writing to a live site needs permissions I had not thought about yet.
What I tried
Read documentation. That is the whole method, and it is the reason to discount everything that follows by some amount I cannot name.
Went through the Cloudflare developer platform product by product. Workers, Containers,
Workflows, Email Routing, Access, Workers AI, Browser Rendering, Durable Objects. The
question of each was whether it earned a place here or was merely capable of holding one.
Read the Claude Code routines documentation for the trigger types. Read signal-cli, its
Docker wrapper and Signal’s terms of service. Read this repository too:
src/content.config.ts, DEPLOY.md, and .github/workflows/rebuild-apex.yml.
Then wrote every claim into a findings file marked either verified or inferred — verified meaning read in the documentation and cited, inferred meaning arithmetic I did over published rates. Five capture paths, compared on two things: how much has to be written, and how many hosts have to stay running.
No code was written. Nothing was deployed. Nothing was measured.
What happened
Five things, and the first one shrank the other four.
Signal cannot live on the platform the rest of the system lives on. There is no
official bot API, so every route runs through an unofficial client that Signal’s terms
tolerate rather than support. That client has to hold a connection open. Workers cannot
hold a socket open at all. Containers can, against their own design: billed per ten
milliseconds of active runtime, built to scale to zero, smallest instance 256 MiB. That
last figure will not hold a JVM.
Roughly $12 a month, then. The money is not the objection — a $5 virtual private server
would do the same job better and more honestly. The objection is that a design with no
always-on host anywhere in it acquires one, and acquires it for the cheapest thing it does.
The requirement that adds a host appears in one path of five. Something has to stay connected. Four of the five capture paths do not need that, and so need no second host. An Apple Shortcut posting to a Worker. An address parsed by an Email Worker. A page behind Access, or a Telegram bot on a webhook. The first of those is about thirty lines.
Signal is about a hundred and fifty, and the rest of its price does not come from staying connected at all. A phone number, a volume holding cryptographic keys, re-linking: those follow from there being no official bot API, and they would be there whether or not anything had to stay awake.
The half I assumed I would build already exists. A cloud session opened from a phone
has this repository in its clone: VOICE.md, the schema, every note published so far.
Nothing to build, nothing to maintain. What it is not is an inbox — it provisions a machine
to hold a one-line thought, and it does not work on the Underground.
Publishing is solved, and half of each note is already machine-checkable. A note is one
MDX file; main merges, Pages builds, and a check_run pokes the apex.
src/content.config.ts rejects a two-digit number, a date that is not YYYY-MM-DD, a
status outside the enum, and a related pointing at a note that does not exist.
PROTOCOL.md says prose has no test that returns pass or fail. The structural half does,
and it already existed before any of this came up.
One step in the design would make the writing better rather than faster. Every other
step only moves text sooner. PROTOCOL.md says never review in the window that produced
the work, and a routine triggered by pull_request.opened is that rule held by
infrastructure instead of by discipline. It would run the self-check from note
003 in a context that never saw the drafting conversation.
A pipeline that carries a thought to a merged note with no decision in the middle is a machine for publishing things I do not believe yet. Under my name, from my account, with nothing anywhere asking me first. The gap between capturing and working is not friction to be removed.
Where it broke
Nothing exists, so every sentence above is documentation and arithmetic. I have not sent a capture, deployed a Worker, fired a routine or lost a thought I can point to. The path I recommend is the one I have written the least code for — a suspicious property for a recommendation to have. Notes 001, 002 and 003 were each missing a control. This one is missing the experiment.
The finding is wider than the thing I read. What the documentation supports is narrow: one requirement — a process that stays connected — appears in one of five paths, and it is the path that adds a host. A Telegram bot is a messaging channel too, and it needs no host at all. So the split is not a property of messaging. It is a property of the one channel I wanted the conversation to happen in, and capture and collaboration are two problems is the reading I put on that.
The premise is a claim about my own behaviour, and I have not measured it. The design assumes thoughts are lost because capture is expensive. I have no count of lost thoughts and no example of a note that failed to get written for want of an inbox. A cheaper inbox may produce nothing but more inbox.
The conclusion is convenient, and this is the second time. It lands on building almost nothing, using a subscription I already pay for and a phone I already carry. Note 002 named this exact shape — an argument arriving at the option I already preferred — and naming it there did not stop it happening here. I cannot separate the channel was doing two jobs from I did not want to run a server.
The cost figures are mine rather than Cloudflare’s. The $6.30 is arithmetic over a
usage pattern I invented. One container awake every hour of the month, at a size I picked
because a JVM does not fit in the smallest one. Cloudflare publishes the rates and not that
total, and I did not test whether the process idles more cheaply than I assumed.
Current view
Provisional, and close to the reverse of what I opened with.
I came in intending to build a Signal bridge and came out wanting one Worker, one Shortcut and one routine — of which only the routine touches the writing at all. The expensive part of this design was one requirement rather than one component. Here it was something has to stay awake, and it belonged entirely to the half of the idea that never needed a conversation.
The portable version, if there is one. When one component is doing two jobs, price the jobs before you price the component. The component looks free because you already own it. The requirement it drags in doesn’t, and that is the part you will still be maintaining in a year.
What survives as a claim is that capture should be dumb. A file on a branch, no session, no agent waiting for it, nothing that starts work until I decide work starts. Whether the split between the two jobs is real, or just the shape of one afternoon’s reading, is not something more reading can settle.
What I’d try next
The cheap one first, because it tests the premise rather than the design.
Post one file to an inbox branch by hand, from the phone, for seven days. No Worker, no
Shortcut, no agent — gh api or the GitHub mobile app, with the branch excluded from Pages
builds so nothing deploys. If the inbox is empty at the end of the week, capture friction
was never the problem and none of the rest is worth building.
Note 001 asked for a number written down in advance and did not get one, so: three items in seven days, and fewer than that means the premise was wrong. The number is a guess. Fixing it before the week rather than after it is the entire point.
The review routine is the only other piece worth building, and only if the first test passes. It is also the one part I could check against something real. Run it over a note that already exists, and see whether a context that never saw the drafting session finds the claim I know is weakest.