tekuti
Tekuti means "I'm telling you" in Oshiwambo. It is weblog software written in Scheme, using Git as its persistent store.
Why
Tekuti hacks the web as it is meant to be hacked.
Tekuti uses tags, not categories. Put however many labels you like on your posts, the more the merrier: with each tag you add meaning to your tag cloud, related posts, and related tags.
Tekuti uses Git as its backing store. The data model scales, and you get hot backups and rollback for free.
All operations on the data store are revertable. Try it: make a post, add some comments, revert some comments, revert the reversions...
Tekuti is secure. There is one entry point, and the entirety of the program is written in the functional style. It does not perform any destructive modifications to data. It is very easy to audit its code.
Tekuti respects the meaning of the web's GET and POST operators. Clicking on a link will not modify the data store.
There is a strict separation in types between XML and strings.You won't see malformed XHTML come out of Tekuti, much less an XSS vulnerability.
Output transformation and templating is easy and built-in to the language via the powerful pre-post-order operator.
And, most importantly, it's written in Scheme, so it's fun to hack!
How
Installing prerequisites
Install Apache. I use the 2.x series.
Install mod-lisp. Mod-lisp is a simple Apache module that offloads request handling to an external process, using a simple key-value protocol. It doesn't actually have anything to do with lisp. It's not widely distributed, so you'll probably have to check it out and build it for yourself:
svn co http://www.fractalconcept.com:8000/public/open-source/mod_lisp cd mod_lisp sudo apxs -i -c mod_lisp2.c
On Debian-derived systems, you'll probably have to use apxs2 on the last line.
Install Guile. Use the latest release in the 1.8 series. Installing it from your distribution's packages is probably sufficient.
Install Guile-lib from its source repository:
bzr branch http://arch.gna.org/guile-lib/guile-lib.bzr/ guile-lib cd guile-lib autoreconf -vif && ./configure --prefix=/usr && make sudo make install
You can install it to another prefix, but in that case you will need to modify GUILE_LOAD_PATH when you run tekuti.
Install git. The latest from the 1.5 series is best. I use git from git, which is kinda fun.
Get tekuti
Tekuti is only distributed from bzr. Currently, I have no intention of making releases, although this may change if there is demand.
bzr get http://wingolog.org/bzr/tekuti
Tell apache how to get to tekuti
Configure apache to load mod_lisp. On Fedora, copy the following lines into a new file, /etc/httpd/conf.d/mod_lisp2.conf:
LoadModule lisp_module /usr/lib/httpd/modules/mod_lisp2.so LispServer 127.0.0.1 8081 "blog" AllowEncodedSlashes On <Location /blog> SetHandler lisp-handler </Location>
If you're on Debian, write those lines into /etc/apache2/mods-available/lisp.load instead, and change the LoadModule line to end with /usr/lib/apache2/modules/mod_lisp2.so, and then run a2enmod lisp.
In both cases, you'll have to restart apache.
Run tekuti
You can run tekuti from its checkout directory:
./env src/tekuti
Now go to your webserver -- localhost, for example -- and go to the /blog URL. Tekuti should pop up with no posts. Visit the admin interface at e.g. http://localhost/blog/admin. The default user is admin and the password is admin.
This sets up tekuti to handle requests under /blog. Optionally you can map certain URLs from another path to /blog, using mod_rewrite:
RewriteEngine On RewriteBase / RewriteRule ^$ /blog/ [QSA] RewriteRule ^archives/?(.*)$ /blog/archives/$1 [QSA] RewriteRule ^feed(/.*)?$ /blog/feed/$1 [QSA] RewriteRule ^search/?$ /blog/search [QSA] RewriteRule ^tags/?(.*)?$ /blog/tags/$1 [QSA] RewriteRule ^debug/*$ /blog/debug [QSA] RewriteRule ^admin/?(.*)$ /blog/admin/$1 [QSA,NE]
Migrating from wordpress
Tekuti includes a script to import posts from wordpress into a suitably-laid-out directory. Run it as:
python ./wordpress-to-dir.py HOST USER PASSWORD DBNAME
Then in the directory that the script makes, run:
git init git add . git commit -m 'initial import from wordpress'
Then replace ~/blog.git with the .git subdirectory.
Tekuti's URL structure is almost the same as Wordpress' so the only other thing you might want to do would be to redirect category links to tag links, and the rss2 feed to the atom feed:
RewriteRule ^archives/category/(.*)$ /tags/$1 [R=permanent] RewriteRule ^feed/rss2$ /feed/atom [R=permanent]
Tekuti will emit valid XML. It will complain if the wordpress posts are not valid. Common errors would be using <br> instead of <br />, and ampersands in URLs. Running xmllint on all files named "content" in the directory might be useful.
The XML code doesn't understand all character entities, either -- see config.scm's *character-entities* to add more.
Hacking tekuti
Install GDS, Guile's emacs integration. Then run Tekuti with the --gds option. Open tekuti/config.scm in Emacs, change the admin password, and press C-x C-e after the closing parenthesis. Then go to your admin page and see if the password changed. Nifty, eh?
Alternately you can run tekuti with the --repl option, although that is less useful. For me, the best way to hack in the repl is to enter "inside" other modules. For example, to change the admin password, I would:
;; enter in the already-defined (tekuti config) module (define-module (tekuti config)) ;; replace the *admin-pass* definition (define *admin-pass* "foo")
Deploying
For deployment-specific variables like the CSS files and the paths and such, you can run tekuti with the -c argument and pass it a config file, which will be evaluated inside the (tekuti config) module. For example, one of mine looks like this:
;; -*- scheme -*-
(set! *navbar-links*
'(("about" . "/about/")
("software" . "/software/")
("writings" . "/writings/")
("photos" . "/photos/")))
(set! *navbar-infix* " | ")I suggest taking the CSS file from my blog and customizing it for your usage. Please make it look different. If you come up with something that I should include as a default, I'd be interested in seeing it.
I deploy tekuti in a screen session. Run screen, then run tekuti. Then press C-a d to detach from the session. Then when you come back to your server you run screen -r to jump back into that tekuti session.
Currently, there are a couple exceptions that cause tekuti to exit. I'll fix these with time, but for now I run it in a while loop:
while true; do ./env src/tekuti; done
Feedback
Send me an email at wingo at pobox.com.