Publishing controls
PULSE drafts a great deal on its own: daily social copy, blog posts, podcast episodes, videos. Whether any of it leaves the building is decided by two layers of switches, and nothing is armed by default.
The rule
armed(channel) = PULSE_APPLY_ENABLED is exactly "true"
AND the channel's own setting is onPULSE_APPLY_ENABLEDis the kill switch. It is off unless it is exactly the lowercase stringtrue. When it is off, no channel can publish, whatever else is set.- Each channel can then be narrowed. A channel with no setting of its own follows the kill switch. A channel setting can only turn a channel off. It can never turn one on when the kill switch is off.
The channels
| Channel key | What it publishes |
|---|---|
blog | A Hugo post on your website, through GitHub. See Site pipeline. |
podcast | Podcast episodes, by adding them to the public RSS feed |
audiobook | The weekly long-form book, in the same feed |
youtube | Real uploads to a YouTube channel, under the operator’s Google identity |
linkedin | Text posts on one personal LinkedIn profile |
instagram | Nothing yet. The uploader is a stub, but the control exists so it cannot publish unguarded later. |
spotify | Nothing is scheduled to it today, but it has a control for the same reason |
Setting a channel
A channel’s own setting comes from the first of these that holds a clear yes or no:
- An environment variable,
PULSE_PUBLISH_<CHANNEL>, for examplePULSE_PUBLISH_YOUTUBE=false. It accepts1,t,true,y,yes,onand0,f,false,n,no,off. - A config file, at
PULSE_PUBLISH_CONFIG_PATH(default/etc/runink/pulse-publishing.json), or the same JSON inline inPULSE_PUBLISH_CONFIG_JSONwhen there is no file. - The built-in default, which is on. Combined with a kill switch that is off by default, nothing publishes until someone arms the global.
The config file accepts either shape:
[{"channel": "youtube", "armed": false}]{"channels": [{"channel": "youtube", "armed": false}]}A channel missing from the file has no opinion. A file that cannot be read or parsed is ignored and changes nothing in either direction.
Checking what is armed
At startup the server logs one line per channel:
PUBLISHCFG youtube: armed=false (...); channel opinion from ...; console config: ...A refused publish logs PUBLISHCFG <channel>: DISARMED — did NOT ..., with the
layer that decided.
PULSE_APPLY_ENABLED=yes, on, 1, or TRUE does not arm anything. The
server warns at startup with PUBLISHCFG-AMBIGUOUS. Write exactly true.What disarming does to queued posts
The kill switch applies at the moment of publishing, not only when a post is
queued. A scheduled post that comes due on a disarmed channel is marked
withheld, with the reason. That is final. Re-arming does not replay the
backlog; schedule the content again to re-queue it.
Approving content does not bypass the switch either. An approval is consent to the content, and the switch is a statement about the channel.
The same switch gates server writes
PULSE_APPLY_ENABLED=true is also what allows the Site Audit’s apply modes
(apply, commit, pr) to write to the server’s filesystem and run git, and
what arms the daily blog drafting in the social engine.
Channel-specific safeguards
| Channel | Also needs |
|---|---|
blog | A GitHub data source with a token, and PULSE_SITE_REPO. |
youtube | YOUTUBE_PRIVACY_STATUS. Unset means private, so an unreviewed upload is never public by accident. |
linkedin | LINKEDIN_ACCESS_TOKEN from a secret (never from the database) and LINKEDIN_AUTHOR_URN for a person. Organization pages are refused. |
podcast, audiobook | PULSE_PUBLIC_BASE_URL, and the episode’s audio must exist. Then submit <PULSE_PUBLIC_BASE_URL>/podcast/feed.xml once to each podcast platform. |