sweetroll 
A website engine for the indie web with curved swords. Curved! Swords!
- uses Git+JSON for storage
- supports Micropub for posting, updating, deleting and undeleting
- allows posting in CommonMark Markdown and other markup languages (powered by Pandoc)
- sends and receives Webmentions, including Salmentions
- supports the webmention-to-syndication / Syndicate by Reference process (Bridgy Publish)
- sends PubSubHubbub notifications on new posts (for readers)
- supports indie-config
- has a JSON Web Tokens-based token-endpoint
- extensible with JavaScript plugins
- written in Haskell
I'm running it on my website.
Privacy notice: if you expose your website's git repo publicly, your "deleted" entries are not really deleted.
Usage
Installing Sweetroll on a server requires some UNIX sysadmin skills. If you can't do it, ask your friends for help or check out other IndieWeb projects: some of them have hosted versions, some run on shared PHP hosting.
First, you need to get a binary of Sweetroll. I haven't uploaded any yet, so you have to build from source.
Buliding from source
- get stack (from your OS package manager or
cabal install stack) - get bower (get node/npm from your OS package manager,
npm install -g bower) git clonethe repocdinto itbower installstack build
When it's done, it says where it put the binary (something like .stack-work/install/your-platform/some/versions/.../bin).
Running on a server
Copy the binary to the server (using scp, usually).
Create a user account on the server (for example, sweetroll).
Create a directory where your website's content files will be, and run git init there.
Make sure the user has read and write permissions on the directory.
And configure your favorite service management program (don't forget to replace everything with your values!) to run Sweetroll as that user!
Here's an example for runit:
#!/bin/sh
umask g+w
exec chpst -u sweetroll /home/sweetroll/.local/bin/sweetroll
--https \ # this means HTTPS is *working*! i.e. you have it set up on your reverse proxy!
--protocol=unix \ # will run on /var/run/sweetroll/sweetroll.sock by default; you can override with --socket
# or: --protocol=http --port=3030 \
--domain=unrelenting.technology \ # your actual domain!
--repo="/home/sweetroll/repo" \ # the site directory! don't forget to run `git init` inside of it first
--secret="GENERATE YOUR LONG PSEUDORANDOM VALUE!...2MGy9ZkKgzexRpd7vl8" 2>&1(Use something like head -c 1024 < /dev/random | openssl dgst -sha512 to get the random value for the secret. No, not dynamically in the script. Copy and paste the value into the script. Otherwise you'll be logged out on every restart.)
Putting a reverse proxy in front of Sweetroll is not required, but you might want to run other software at different URLs, etc. I wrote 443d as a lightweight alternative to nginx.
After you start Sweetroll, open your new website.
It should write the default configuration to conf/sweetroll.json in your site directory.
Edit that file, you probably want to change some options.
Create a templates directory in your site directory.
You can override the HTML templates you see in this repo's templates directory with your own using your templates directory.
The templating engine is embedded JavaScript via lodash's _.template.
You need to put your h-card and rel-me markup into templates/author.ejs.
Restart Sweetroll after any changes to the config file or the templates.
Use Micropub clients like Micropublish and Quill to post.
Development
Use stack to build (and bower to get front-end dependencies).
Use ghci to run tests and the server while developing (see the .ghci file).
The :serve command in ghci runs the server in test mode, which means you don't need to authenticate using IndieAuth.
$ bower install
$ stack build
$ stack test
$ (mkdir /tmp/sroll && cd /tmp/sroll && git init)
$ stack ghci --no-load
:serve
$ http -f post localhost:3000/login x=x | sed -Ee 's/.*access_token=([^&]+).*/\1/' > token
$ http -f post localhost:3000/micropub "Authorization: Bearer $(cat token)" h=entry content=HelloWorld(the http command in the examples is HTTPie)
Libraries I made for this project
- gitson, a git-backed storage engine
- pcre-heavy, a usable regular expressions library
- http-link-header, a parser for the Link header (RFC 5988)
- microformats2-parser, a Microformats 2 parser
- indieweb-algorithms, a collection of implementations of algorithms like authorship and link discovery
- hs-duktape, Haskell bindings to duktape, a lightweight ECMAScript (JavaScript) engine
TODO
- html/frontend/templating
- remove prev/next navigation
- de-twitterize emoji in twitter reposts
- show recursive reply contexts
- support emoji reactions like here ?
- support WebFinger with HTML as the source of truth + additional links from config e.g. for remoteStorage
- figure out URL/canonical/etc. handling for alternative networks & mirrors like .onion & IPFS -- including webmentions!
- custom non-entry html pages
- archive pages, ie. unpaginated pages (basically
?after=0&before=9223372036854775807but... "archive" design?) - indieweb-components: a component for a Medium-style popup on selection that offers a fragmention link and (?) indie-config repost-quote-something (look how selection-sharer works on mobile!! but probably should look the same just at the opposite direction than iOS's popup)
- built-in TLS server, since we depend on
tlsalready because of the client
- event system: hooks on micropub posting and webmention processing
- cleaning a cache (which is not there yet... should be an in-process cache with fast expiration -- protection against DDoS or Hacker News effect)
- real-time page updates with Server-Sent Events (make a Web Component that will show the update button)
- static mode: on these events, regenerate website pages into static HTML files (and serve them for better performance)
- IPFS support! (see/improve hs-ipfs-api) publishing there in the event handler too. Oh, and IPFS supports custom services! IPFS-Webmention, because why not.
- S3 support & running on AWS Lambda... or good old CGI, which is actually kinda similar to Lambda
- JS hooks as plugins
- call from events
- post category decisions
- config getting
- HTTP request sending
- HTTP request (webhook) handling
- example plugin: Pushover notifications
- example plugin: Telegram bot (posting, webmention notifications, responding to them, deleting them, etc.)
- webmention (YAY W3C DRAFT!)
- moderation tools
- different modes in config: allow all (except blocked), allow known good domains (e.g. domains replied to), premoderate all, turn off webmention
- blocking domains
- sharing block lists
- reverify/refetch to update user profiles and stuff
- deduplicate threaded replies like there (a reply to both my post and another reply) -- maybe that's already happening? need to test
- deduplicate syndicated replies
- moderation tools
- micropub (YAY W3C DRAFT!)
- handle update requests
- handle delete requests
- handle undelete requests
- support posting photos
- download / locally store / rewrite url of photos given as urls
- indieweb-algorithms?: ensure the person you're replying to never gets picked up you when you're replying (caught in test without own h-card)
- tags? (kill the difference between categories and tags? // use symlinks to add to multiple categories/tags)
- extract a
WebPreludepackage:Sweetroll.Prelude,Sweetroll.HTTPClient,formToObject, more stuff
License
This is free and unencumbered software released into the public domain.
For more information, please refer to the UNLICENSE file or unlicense.org.