Prerequisites
create-postavailable at~/.local/bin/create-post(symlinked from~/av/bin/hydra/publish/)- Python 3.11+
~/av/prj/archerships.com/checked out and up to date~/av/bin/archerships/on your PATH or called with full pathhydra-publishavailable at~/.local/bin/hydra-publish- Brave running with remote debugging enabled (required for Facebook, Substack, Twitter cross-posting):
open -a "Brave Browser" --args --remote-debugging-port=9222
Step 1: Create a post stub
Run create-post with the post title and section:
create-post "My Post Title" --section accelerationValid sections: acceleration, tech, seasteading.
This creates one file:
~/av/doc/posts/YYYY-MM-DD-my-post-title/
YYYY-MM-DD-my-post-title.md
The .md file contains a frontmatter stub:
---
type: long-form
purpose: explainer
title: "My Post Title"
subtitle: ""
description: "My Post Title -- archerships"
date: 'YYYY-MM-DD'
section: acceleration
tags: []
ai_percentage: 0
published_at: []
---Before writing, set the correct type and purpose. Valid types: short-form, long-form, recipe, table, lightbox, handoff. Valid purposes: coolshare, explainer, propaganda, tutorial, research-brief, tech-brief. See ~/av/pol/WRITING.md for full reference.
Also set ai_percentage (0-100, round numbers): the approximate fraction of text that was AI-generated or AI-edited.
Step 2: Write the post body
Open the .md file and write after the closing --- of the frontmatter.
~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.md
Rules:
- Do not include an H1 (
#) in the body – the renderer generates it fromtitle:. - Use
##for top-level sections,###for subsections. Never skip heading levels. - Leave one blank line before and after every heading, list, code block, and table.
- Verify every link before writing it to the file – no tracking parameters.
- For
propagandaposts: include a## Call to Actionsection at the end. - For
research-briefposts: include a## Sourcessection; note confidence levels. - For
tech-briefposts: prefer tables over prose; end with a## Referencesection. - For
tutorialposts: include a## Prerequisitessection first; number steps within each##section; end with a verification step.
Step 3: Add images
All source images are stored in ~/av/ast/img/ under a short descriptive directory name. Each image directory holds the original plus platform-optimized variants named descriptive-name-platform-WIDTHxHEIGHT.ext.
3.1 Store the original
mkdir -p ~/av/ast/img/deer
cp ~/Downloads/deer-photo.jpg ~/av/ast/img/deer/deer-original.jpg3.2 Create platform variants
Generate a variant for each platform you intend to publish to. Dimensions are defined in ~/av/pol/TWITTER.md, ~/av/pol/FACEBOOK.md, ~/av/pol/SUBSTACK.md, ~/av/pol/NOSTR.md, and ~/av/pol/ARCHERSHIPS-IMAGE.md.
Common target sizes:
| Platform | Dimensions | Format |
|---|---|---|
| archerships | 800 x 450 | webp |
| 1200 x 675 | png | |
| 1200 x 630 | png | |
| substack | 1200 x 675 | png |
| nostr | 1200 x 675 | png |
Use the letterbox/pillarbox rule from ~/av/pol/SOCIAL-SETTINGS.md – scale to fit, center on black background, never crop or stretch.
For archerships.com, convert to WebP and compress under 100 KB:
cwebp -q 82 deer-original.jpg -o ~/av/ast/img/deer/deer-archerships-800x450.webpAdjust quality downward if the file exceeds 100 KB. Do not go below -q 60.
3.3 Symlink into the post directory
ln -s ~/av/ast/img/deer/deer-archerships-800x450.webp \
~/av/doc/posts/YYYY-MM-DD-my-post-title/deer-archerships-800x450.webp3.4 Reference in Markdown
Place the image reference in the body. Banner images go first, before any text:
{alt="A deer standing in a meadow at dusk, facing left" width="800" height="450" loading="lazy"}Rules:
alt=must be a complete readable transcript of the image content.width=andheight=must match the actual pixel dimensions of the file.loading="lazy"on all images not in the first viewport.
Step 4: Render for archerships.com
The renderer to use depends on the post type:
| type | renderer |
|---|---|
long-form, short-form, recipe, handoff |
render-post.py |
table |
render-table.py |
lightbox |
render-lightbox.py |
# long-form, short-form, recipe, handoff:
python3 ~/av/bin/archerships/render-post.py \
~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.md
# table:
python3 ~/av/bin/archerships/render-table.py \
~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.mdThis writes YYYY-MM-DD-my-post-title.html alongside the .md source.
The preview-essay command auto-detects the post type, renders, and opens the result in Brave with browser-sync live reload:
preview-essay ~/av/doc/posts/YYYY-MM-DD-my-post-title/YYYY-MM-DD-my-post-title.mdStep 5: Build
Run the full site build to copy the rendered post into dst/ and regenerate feeds, home page, and indexes:
bash ~/av/bin/archerships/build.shOnly posts with a rendered .html file are included – drafts (no .html) are naturally excluded. The build output lands in ~/av/prj/archerships.com/dst/.
Essays and table posts go to dst/essays/. Imported LJ/FB archive posts go to dst/posts/.
Step 6: Deploy to archerships.com
Architecture overview
The site has two serving layers:
- Nginx on the gateway server serves
dst/from local disk. Updates land here immediately via rsync – the site goes live in seconds. - Arweave stores every file permanently. The ARNS record (
archerships.arweave.net) and the gateway’sAPEX_TX_IDpoint to the latest Arweave manifest. Arweave propagation takes 15-60 minutes but runs in the background; the site is already live from nginx before it completes.
6.1 Deploy a new post (fast path)
Use --new-files to deploy only the new post’s files. This skips hashing the full archive (35K+ files) and makes the post live in seconds.
cd ~/av/prj/archerships.com
node ~/av/bin/archerships/deploy-website.mjs \
--new-files "essays/YYYY-MM-DD-slug.html,essays/image.webp,index.html,feeds/all.xml,feeds/acceleration.xml" \
--yesAdjust feeds/acceleration.xml to match the post’s section: - acceleration posts: feeds/acceleration.xml - tech posts: feeds/tech.xml - seasteading posts: feeds/seasteading.xml
The deploy script will: 1. Rsync the specified files to the gateway (site live immediately). 2. Upload them to Arweave (background, free tier for files under 100 KB). 3. Upload an updated manifest and update the ARNS record. 4. Spawn inject-arweave-links.py in the background to add permanent Arweave links to the post footer once the txIds resolve on arweave.net.
6.2 Deploy everything (full deploy)
Only needed when many files change at once (new section, redesign, etc.):
cd ~/av/prj/archerships.com
node ~/av/bin/archerships/deploy-website.mjs --yesThis walks the full dst/ tree, which takes several minutes to hash.
6.3 If Arweave propagation times out
The deploy script waits up to 5 minutes for the manifest to appear on arweave.net. If it times out, the site is still live from nginx. Once arweave.net has the data, finish the ARNS update with:
cd ~/av/prj/archerships.com
node ~/av/bin/archerships/deploy-website.mjs --set-arns <manifestTxId> --yesThe manifest txId is printed by the deploy script when it uploads the manifest.
6.4 Inject Arweave permalink footer (manual)
inject-arweave-links.py runs automatically after a --new-files deploy. To run it manually (e.g., after a --set-arns or --full deploy):
python3 ~/av/bin/archerships/inject-arweave-links.py essays/YYYY-MM-DD-slug.htmlThe script polls arweave.net until the txId resolves, then injects a footer into the HTML with both a direct Arweave link and an ARNS link, and rsyncs the patched file to the gateway. Visitors can use these links to retrieve the content even if archerships.com goes offline.
Step 7: Cross-post to other platforms
hydra-publish YYYY-MM-DD-my-post-title \
--platforms nostr,substack,facebook,twitter-articlehydra-publish reads the .md frontmatter for metadata, publishes to each platform, and records the resulting URL and ID back into published_at in the frontmatter.
For long-form posts, always use twitter-article (not twitter) – Twitter Articles display the full essay; a tweet would only carry the title and link.
Platform-specific notes:
- Nostr: URL is computed via
nak encode naddr– never construct it by hand. - Substack: post is created as a draft; you publish it manually in the Substack editor.
- Facebook: Brave must be running with
--remote-debugging-port=9222.
Verification
- Confirm the post loads at
https://archerships.com/essays/YYYY-MM-DD-slug.html. - Check the footer has “Permanently archived on Arweave” links (added by
inject-arweave-links.pyonce Arweave resolves – may take up to an hour). - Verify images render correctly with no broken links.
- Confirm the post appears in
https://archerships.com/feeds/all.xml. - Confirm
published_atin the frontmatter has entries for each platform posted to.
Want to stay in touch?
- Join my Signal announce-only group to be notified when I have a new essay up and other important announcements.
- For discussion, join the libertygardeners Signal group.
- Subscribe to my mailing list.
- Email: [email protected]
- Signal: archerships.43
- Website: archerships.com
- Other social media: Substack | Twitter | Facebook | Nostr | Odysee
If you’d like to support my work:
- Share my posts.
- Become a subscriber to my newsletter.
- Attend my live events (dinner parties, conferences, pop-up cities, etc).
- Introduce me to like-minded people.
- Make a one-time donation to support my work: Crypto | Fiat
- Hire me for privacy / crypto / censorship consulting.
If there is a topic you’d like me to cover, please let me know!
Questions, comments, and suggestions are welcome.