Social publishing has a strange split in it.
The words may live in a document. The design lives in another tool. The schedule lives in a third tool. The credentials are somewhere else. Then, after a post goes out, the only reliable record of what happened may be the post itself.
I wanted that whole process to behave more like code.
Not because every social post needs a pull request and a committee. I wanted plain files I could inspect, rules I could run before publishing, repeatable visuals, and a history that did not depend on remembering which browser tab I used last week.
That became Postline, a Git-native CLI for planning, checking, rendering and publishing branded social posts.
The post is the source of truth
A Postline post is a Markdown file. The body contains the copy. Front matter contains the schedule, channels and optional card instructions.
markdown--- at: 2026-10-27 09:00 channels: [linkedin, bluesky] topic: release-notes card: layout: rows eyebrow: Product update headline: - What changed this week items: - title: Faster publishing note: No GitHub Actions required chip: shipped footer: postline --- Postline can now run its scheduler on your own machine.
That file can be reviewed, diffed, searched and restored. Channel-specific variants can stay beside the canonical copy instead of drifting into separate scheduler drafts.
The repository becomes the calendar, but Git is not the scheduler. Postline still has to understand when a post is due, whether it is valid, what image belongs to it and whether it has already been sent.
The design belongs in the same system
I did not want the visual side to mean exporting a fresh image by hand for every post.
Postline has a small design system. The project configuration defines the brand name, logo, colours, font, chip tones and default image settings. Each post chooses one of three layouts: rows, grid or statement. It can also select a square, portrait or landscape output.
This is deliberately constrained. A renderer becomes useful when the same input produces the same result. Postline is not an AI designer trying to invent a new composition on every run. It is a configurable renderer that applies a brand system reliably.
That distinction matters. Generative design can be useful when exploration is the goal. Scheduled publishing usually needs consistency. The logo should not wander. The colour assigned to a warning should not change because the model felt creative. A long headline should be caught before it becomes tiny text in a crowded card.
How a card becomes an image
The rendering pipeline is ordinary on purpose:
textbrand config + post card instructions -> validation and linting -> escaped HTML and CSS -> embedded logo and font assets -> exact output viewport -> Chromium screenshot -> PNG or JPEG
The renderer first combines the post's card section with the brand configuration. Local logos and fonts are converted into data URIs, so the generated card does not depend on a temporary file server. Text is escaped before it reaches the HTML template.
Postline then hashes the generated HTML and render settings. If nothing changed and the image still exists, it reuses the cached card. If something changed, Chromium opens a page at the exact dimensions for the chosen format, waits for the page and fonts to settle, then captures the image.
The current sizes are explicit:
- 1200 by 1200 for square cards;
- 1080 by 1350 for portrait cards;
- 1200 by 627 for landscape cards.
There is also a --html option. It writes the intermediate card HTML beside the image, which is much nicer when adjusting typography or spacing than repeatedly staring at a PNG and guessing which rule caused the problem.
Publishing is where state starts to matter
Rendering a card is deterministic. Publishing it is not.
Networks time out. Credentials expire. A process can stop after the remote platform accepted a post but before the local program recorded the response. Blindly retrying that situation is how duplicate posts happen.
Postline keeps a ledger for this reason. Before a real send, it records that the attempt is in progress. After the channel responds, it records the published result and remote identifier. If a previous run stopped in the uncertain middle state, Postline blocks instead of pretending it knows whether the post went out.
The recovery step is intentionally human:
textcheck the channel -> confirm whether the post exists -> mark the outcome or explicitly retry
That is less magical than an automatic retry loop. It is also safer.
The publishing core stays separate from network-specific details. LinkedIn, Bluesky, Mastodon, webhooks and file output each have an adapter. The core decides what is due, validates it, prepares the media and updates the ledger. The adapter handles the shape and authentication rules of its platform.
GitHub Actions is optional
The first obvious place to run a Git-native publishing tool is CI. That works, but it should not be the only answer. Not everyone has GitHub Actions credits, and a personal publishing workflow should not stop because a hosted runner is unavailable.
Postline includes a persistent daemon:
shnpm install --global https:"syntax-comment">//github.com/1cbyc/postline/releases/download/v2026.0.1.0/postline-0.1.0.tgz postline daemon --every 15
It checks immediately, then checks again every 15 minutes. Runs are serial, so a slow publish cannot overlap the next cycle. An individual failure is reported without killing the daemon, and normal process signals stop it cleanly.
That means it can run on an always-on laptop, a small VPS, a home server or under systemd. The generated GitHub Actions workflow is still available for people who want it. It is one runner option, not a requirement built into the product.
What Postline designs, and what it does not
Postline can produce the final visual that gets attached to a post. It applies the logo, palette, font, spacing, labels, footer and status chips. It lays content out as rows, a grid or a statement. It renders the final image and hands it to the publishing adapter with appropriate alternative text.
It does not currently look at a topic and invent an entirely new art direction. The user defines the brand and layout language. Postline turns that language into repeatable output.
I like that boundary for this tool. The interesting promise is not that every post will surprise you. It is that the content can move through one understandable pipeline:
textwrite -> check -> render -> schedule -> publish -> record
There are fewer hidden tabs in that model. If a rule changes, it is in the config. If the copy changes, it is in the post. If the visual changes, the new image can be reproduced. If publishing fails, the ledger has evidence instead of a vague memory.
That is what I wanted from social publishing: less ceremony, but more truth about what the system actually did.