The post that waited for a terminal

Part 2 of 2 of ./blog.sh v1.7

The post is written on the tram: a title, four paragraphs, two photographs with their descriptions. The switch above Send says Draft, which is what it says unless it is moved. Send, and the answer card comes back with the preview address. Then it sits. Putting a draft out was a command, and a command wants a terminal, and the tram has none.

1.6 asked for that decision at the wrong end: publish at the moment of sending, before the blog has rendered anything, or see the blog's own page and then find a keyboard. 1.7 closes that, and then closes the road the answer takes back.

One file, down the same connection

The answer card for a draft now carries a Publish button. Pressing it sends one file, called publish.txt, holding the slug and nothing else -- through the same share sheet, the same two shortcuts, the same SSH connection the post itself took.

The writing page at /write/ on a phone, with the text, tags and the Send button
The page a post is written on: the same page on any phone, and the only one the engine publishes as a file.

The receiver knows that shape: one file in the delivery, called that. It stores nothing. It reads the slug and runs publish <slug> --yes --json. The slug becomes an argument to a command, so it is checked as hard as a filename is -- lower-case letters, digits and dashes, no leading dash to be read as a flag, no newline in the middle to glue two lines into one word -- and anything else is refused here rather than explained by whatever it hits. A publish.txt arriving beside a markdown file is not a request; it is stored like any other file.

publish --yes --json answers as one object -- slug, path, state, url, deploy, warnings, the shape add --json prints -- and leaves with zero whatever it says, because iOS Shortcuts throws away the output of a remote command that failed. Press the button twice and the second press is answered rather than obeyed: already_published, with the address.

An answer the page goes and gets

The road back has one break in it that nothing on the server can mend. The reply travels as a URL, and a page kept on a phone's home screen runs with storage of its own: the URL opens in the browser, where the draft it is about does not exist. The draft stays on the home-screen copy, looking unsent. A phone that goes into a lift loses the same answer for a duller reason.

So the page stops waiting. Before it sends anything it picks a name for its answer -- sixteen hexadecimal characters out of the browser's random source, a fresh one for each send -- and writes it into the post as receipt:. The build then leaves a small file at /write/r/<name>.json. Here is one, whole:

{"slug":"on-the-tram","state":"draft","title":"On the tram","url":"https://example.com/draft/41a0a9b77caef98a/on-the-tram/","warnings":[]}

The page asks for that address every three seconds for five minutes, and says so if it never comes: a page that gave up in silence would be indistinguishable from a post that never arrived. Press Publish and it asks the same name again until the state says published.

The build writes it, which is what keeps it true: publishing the post rewrites the same file, the draft's preview address giving way to the public one; deleting the post stops it being generated and the sweep takes it away. An answer arriving is itself the proof that the build and the upload went through.

What is in it, and what is not

Five things: the slug, the state, the title, the address, and whatever the save had to complain about -- a picture whose size could not be read, a video that will make the reader wait. The phone is the one place with no terminal to read those in.

Nothing else, on purpose. Not the path the post has on the server. Not what the run said about the site rather than about the post -- a missing base_url, whatever the rebuild warned about -- because the file sits at a public address and its only protection is that its name is sixteen random characters. Anyone who has them can read it. The draft preview it names is the address the draft is readable at anyway.

What it still refuses to do

Anything about a post already on the blog. The page cannot open one, edit one, delete, rename, schedule, or take a published post back down. It writes one post, sends it, and offers to publish that one -- and only that one, because publishing takes the receipt this page minted. It is the 1.6 design with one step added, not a console growing on the site.

What it costs

Pressing Publish announces. It is the road publish takes at a desk -- the date settled, the post out on whatever networks the site has configured, the site rebuilt and deployed -- and there is no undo on the phone.

The five minutes run from the send, not from the reload: a page reopened an hour later does not resume asking. And on a site whose deploy is owed to the next scheduled run, the file on the server can be older than the page's patience. The page then says there is no answer yet, that the blog may still be building, and to go and look before sending again -- which is not the same sentence as "it failed", and usually it was not.

Where the page is

It runs on this site, at /write/. It is a demonstration, and it keeps nothing: what you type stays in your own browser, and sending needs a key that lives in a shortcut on the phone of whoever runs the blog. Open it, write in it, and nothing here moves.

Comments