diff options
author | James Vasile <james@hackervisions.org> | 2011-02-22 13:32:45 -0500 |
---|---|---|
committer | James Vasile <james@hackervisions.org> | 2011-02-22 13:32:45 -0500 |
commit | 35071d7212cec1fc23e8204bfd392a116a5313ed (patch) | |
tree | 1c75a525227769fc94f303b5c0233882d90ef2a8 /doc |
...
Diffstat (limited to 'doc')
-rw-r--r-- | doc/Makefile | 118 | ||||
-rw-r--r-- | doc/colophon.mdwn | 8 | ||||
-rw-r--r-- | doc/design.mdwn | 196 | ||||
-rw-r--r-- | doc/faq.mdwn | 37 | ||||
-rw-r--r-- | doc/footer.html | 0 | ||||
-rw-r--r-- | doc/hacking.mdwn | 97 | ||||
-rw-r--r-- | doc/header.html | 0 | ||||
-rw-r--r-- | doc/modules.mdwn | 71 | ||||
-rw-r--r-- | doc/roadmap.mdwn | 64 | ||||
l--------- | doc/style.css | 1 | ||||
-rw-r--r-- | doc/themes.mdwn | 59 |
11 files changed, 651 insertions, 0 deletions
diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..2e6037a --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,118 @@ +DOCDIR=../dist/doc + +PANDOC=pandoc +PDFLATEX=pdflatex + + +# List text files in the order in which you want them to appear in the +# complete manual: +SOURCES=README.mdwn INSTALL.mdwn themes.mdwn hacking.mdwn TODO.mdwn modules.mdwn scripts.mdwn design.mdwn roadmap.mdwn faq.mdwn COPYING.mdwn colophon.mdwn +TODO_SOURCES=$(patsubst TODO.mdwn,,$(SOURCES)) +MAN_SOURCES=$(patsubst COPYING.mdwn,copyright_notice00,$(SOURCES)) + +NEWLINE_SOURCES=$(patsubst %,% oneline.txt,$(SOURCES)) +NEWLINE_MAN_SOURCES=$(patsubst %,% oneline.txt,$(MAN_SOURCES)) + +HTML=plinth.html $(patsubst %.mdwn,%.html,$(SOURCES)) +HTML_PART=$(patsubst %.html,%.part.html,$(HTML)) +LATEX=plinth.tex $(patsubst %.mdwn,%.tex,$(SOURCES)) +PDF=plinth.pdf $(patsubst %.mdwn,%.pdf,$(SOURCES)) +MAN=plinth.1 + +OUTPUTS=$(HTML) $(LATEX) $(MAN) $(PDF) $(HTML_PART) +DIST_OUTPUT=$(patsubst %,$(DOCDIR)/%,$(OUTPUTS)) + +# Yes, do it twice. TODO created during the process requires a second run +default: + make all + make all +all: oneline.txt $(OUTPUTS) Makefile + +$(DOCDIR)/%: % + cp $< $@ +dist: $(DIST_OUTPUT) +############################################################################### +oneline.txt: Makefile + perl -e 'print "\n"' > oneline.txt + +$(SOURCES): + @rm -f $@ + @ln -s ../$(patsubst %.mdwn,%,$@) $@ + +../TODO : $(TODO_SOURCES) ../*.py ../modules/*.py ../Makefile Makefile ../templates/Makefile + grep -ro --exclude=.git* --exclude=plinth.1 --exclude=*.tex --exclude=*.html \ + --exclude=README.mdwn --exclude=INSTALL.mdwn \ + --exclude=TODO.mdwn --exclude=COPYING.mdwn \ + "TODO\:.*" ../* 2>/dev/null | \ + sed -e "s/TODO\://g" | \ + sed -e "s/^..\//* /g" | \ + sed -e 's/"""$$//g' | \ + sed -e 's/<\/p>$$//g' \ + > ../TODO +############################################################################### +## +## MAN PAGES +## +$(MAN): $(SOURCES) ../TODO + @csplit -s -f copyright_notice COPYING.mdwn '/##/' + cat $(NEWLINE_MAN_SOURCES) | perl -pe 'BEGIN { $$/=undef } $$_ =~ s/\n\n#\s.*/\n/gm; $$_ =~ s/\n\n#/\n\n/gm; $$_ =~ s/(\n\n#\s.*)/uc($$1)/gme' > .make_man + $(PANDOC) -s -t man -o $@ .make_man + @rm -f copyright_notice0? .make_man +manpages: $(MAN) +############################################################################### +## +## LaTeX +## +%.tex: %.mdwn + $(PANDOC) -s --toc -f markdown --standalone -o $@ $< + +hacking.tex: hacking.mdwn ../TODO + $(PANDOC) -s --toc -f markdown -o $@ hacking.mdwn ../TODO + +plinth.tex: $(NEWLINE_SOURCES) ../TODO + $(PANDOC) -s --toc -f markdown -o $@ $(NEWLINE_SOURCES) + +latex: $(LATEX) +############################################################################### +## +## HTML +## + + +# This gets us the html sections complete with TOC, but without the +# HTML and head section boilerplate. /help/view uses the parts. +%.part.html: %.html + csplit -s -f $@ $< '%.*<body>%' + sed '1d' $@00 > $@01 + csplit -s -f $@ $@01 '/<\/body>/' + mv $@00 $@ + rm $@01 +%.html: %.mdwn header.html footer.html style.css Makefile + $(PANDOC) -s --toc -c style.css -f markdown -o $@ header.html $< footer.html + +hacking.html: hacking.mdwn ../TODO style.css Makefile + $(PANDOC) -s --toc -c style.css -o $@ -f markdown hacking.mdwn ../TODO + +#plinth.html: $(NEWLINE_SOURCES) ../TODO style.css Makefile +# $(PANDOC) -s --toc -c style.css -o $@ -f markdown $(NEWLINE_SOURCES) + +plinth.html: $(NEWLINE_SOURCES) ../TODO style.css Makefile + @csplit -s -f copyright_notice COPYING.mdwn '/##/' + $(PANDOC) -s --toc -c style.css -o $@ -f markdown $(NEWLINE_MAN_SOURCES) + @rm -f copyright_notice0? .make_man + +html: $(HTML) $(HTML_PART) +############################################################################### +%.pdf: %.tex + $(PDFLATEX) -interaction=batchmode $< >/dev/null + $(PDFLATEX) -interaction=batchmode $< >/dev/null # yes, do it twice so the toc works + +pdf: $(PDF) +############################################################################### + +clean-latex: + rm -f *.log *.out *.aux *.toc + +clean: clean-latex + rm -f $(OUTPUTS) README.mdwn INSTALL.mdwn TODO.mdwn COPYING.mdwn \ + copyright_notice0? \#*\# ../TODO oneline.txt diff --git a/doc/colophon.mdwn b/doc/colophon.mdwn new file mode 100644 index 0000000..8ab6932 --- /dev/null +++ b/doc/colophon.mdwn @@ -0,0 +1,8 @@ +# Colophon + +This manual was typed in emacs, formatted using markdown and converted +to pdf, html, troff and latex using pandoc and pdflatex. + +The complete source code to this manual is available in the 'doc' +directory of the Freedom Box front end source distribution. + diff --git a/doc/design.mdwn b/doc/design.mdwn new file mode 100644 index 0000000..acaa17e --- /dev/null +++ b/doc/design.mdwn @@ -0,0 +1,196 @@ +# Freedom Box Design and Architecture + +This article describes and documents architectural considerations for +my vision of Freedom Box. It is not specific to the user interface +and instead contains thoughts about the Freedom Box as a whole. + +The major immediate design problems that I see are authentication, ip +addressing, and backup. I'm sure there are others. + +If the Freedom Box front end pulls together basic pieces and +configures them properly, we'll have the start of the freedom stack. +Then we can build things like free social networking applications on +top. + +## Design Goals + +The target hardware for this project is the +[GuruPlug](http://www.globalscaletechnologies.com/t-guruplugdetails.aspx). +It has low power consumption, two wired network interfaces, one +wireless hostapd-capable interface (802.11b), and two usb slots for +connecting external drives and peripherals. The hardware target might +change if a better unit becomes available (for example, with 802.11n +or USB 3 capability). The GuruPlug is reputed to have availability +problems involving shipping delays, and targetting a unit that cannot +satsify high demand might be an issue. + +Freedom Boxes are not giant honking servers. They are small boxes +that do perform a lot of functions. They are not heavily resourced. +So keep things small and light. + +By the same token, there is no need to scale up to thousands of users. +A box should happily serve a person and her family, maybe an extended +group of friends. Call it 100 people at the most. + +The target user for this project is the home consumer of network +appliances. It ranges from the most basic user (the kind of person +who might have spent all of three minutes setting up her LinkSys +WRT54G) to the home enthusiast who wants a local file server for media +and backups, print server and dns service. + + +## Authentication + +Authentication in the context of the Freedom Box is a diffiult +problem. You need to be able to trust your box, and it needs to be +able to trust you. What's more, security must withstand forgotten +passwords, server destruction, changing email addresses and any other +possible disaster scenario. + +In addition, your friends (and their boxes) need to trust your box +when it acts on your behalf, even if it does so when you're not around +to enter passphrases or otherwise confirm agency. But even as it +needs to operate in your absence, it can't itself have authority to +access all your data, because you might lack exclusive physical +control of the box (e.g. if you have a roommate). If the box can act +as you without limits, anybody who takes control of the box takes +control of your online identity. + +What's more, security should be high. Freedom Boxes might contain +emails or other sensitive documents. The box mediates network +traffic, which means rooting it would allow an attacker to spy on or +even change traffic. Demonstrating control of an email address should +not be enough to gain access to the box and its admin functions. + +### Passphrases + +Most users habitually think of passwords. We need to force them to +think of passphrases. It starts by using 'passphrase' in the +literature. Second, we need to encourage users to pick phrases, and +prompts should urge them to do so. We also need minimum password +lengths and maybe even a requirement of at least a few spaces. + +Even better than passphrases are passfiles. We should keep a lookout +for ways to use files instead of phrases when securing the Freedom +Box. + +### A Scheme for Secure Web Login + +Passphrases should +[never be stored in plain text](http://www.codinghorror.com/blog/2007/09/rainbow-hash-cracking.html). +[MD5 is too fast for passphrases.](http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html) +Therefore, my current plan for secure website login involves bcrypt: + +The server sends the client a permanent salt, PS, a random session +salt, SS and a number of rounds, R. It brcypts the password using PS +and stores the result, B. + +The browser +[bcrypts the passphrase using javascript](https://code.google.com/p/javascript-bcrypt/) +and PS. Then, it bcrypts that result with SS. It does R rounds of +bcrypt with S. The browser then sends the result, C, back to the +server. + +The server retrieves bcrypts its stored, hashed passphrase and does R +rounds of bcrypt on it with SS. If that matches C, the passphrase is +a match. + +The server must be able to keep track of sessions, as it needs to +remember SS between giving it to the client and getting it back. The +Server cannot rely on the client to remind it of SS. This would allow +an attacker to dictate SS and leave the server vulnerable to replay +attacks. + +This scheme has the dual advantage of not storing any cleartext +passphrases and also never sending any cleartext passphrases between +client and server. + +TODO: consult a security expert as to the strength of this scheme + +### Other Schemes + +#### Monkeysphere + +[Monkeysphere](http://web.monkeysphere.info/) is a promising project. +Monkeysphere's major win is that it avoids the clunkiness of SSL +certificate authorities. That's huge, except it replaces the +certificate authority structure with the PGP web of trust. Relying on +the web of trust is a good idea +([much better than relying on SSL certificate authorities](http://www.crypto.com/blog/spycerts/)), +except that new users might not have joined the web. It also requires +a bunch of GPG infrastructure that might be difficult to setup +automatically and with little user intervention. + +As of this writing, it does not appear to support Windows or Macintosh +clients, making it unsuitable for this project. + +#### Secure Remote Password + +[SRP](http://srp.stanford.edu/) is an interesting technique. Patents +are a concern. What does it buy us that my scheme, above does not? + +There is a +[python implementation](http://members.tripod.com/professor_tom/archives/srpsocket.html), +although it is not clear that it has been through the crucible. Even +if SRP is solid, this implementation might be flawed. + + + +## Finding Each Other + +### Dynamic DNS + +Each box might need multiple dynamic DNS pointers. Every box is going +to require its own set of dynamic names. For example, my Freedom Box +might be known as both `fb.hackervisions.org` and +`james.softwarefreedom.org`. My work colleagues might know me as +`james@james.softwarefreedom.org` while my friends might reach me at +`james@fb.hackervisions.org`. By default, when contacts come in via +different names, my box might be smart enough to treat those contacts +differently. + +If I share this box with my partner, Emily, she might be +`emily@jv187.fboxes.columbia.edu`, in which case my box will need +another dynamic DNS pointer. + +### Mesh Networking + +Freedom Boxes should be meshed. Rather than run in infrastructure +mode, they should route for each other and create strong local meshes +that will carry data for anybody that passes through. + +There are some problems, though. + +* I'm not sure how well nodes in ad-hoc mesh mode interact with +others in infrastructure mode. + +* The Freedom Box only has one wireless radio, which will drastically +hurt speed. + +* Routing can be difficult when connecting mesh network nodes to the WAN. + +## Backup + +Everybody should automatically and redundantly store encrypted backups +on their friend's freedom boxes. Recovery should not assume you kept +your key or know a password. We can recover the decryption key by +having a bunch of friends encrypt the key in various combinations. If +enough of my friends get together, they can decrypt it for me. +Optionally, a passphrase can serve to slow my friends down if they +turn against me. Maybe it takes 5 friends acting in concert to +recover my key, but only 3 to recover a version protected by the +passphrase. + +## Push vs Pull + +For a social network, real time communication is key. Asynch +communication is good, but sometimes people just want to bash comments +back and forth. For that, you need push, which is just a web fetch +meaning "pull my rss feed". + +If, however, you have a lot of friends and they have a lot of friends, +your extended network can be large, resulting in many "pull my rss +feed" commands. We should only process push requests for friends to +reduce load. You really don't need real time updates of the social +networking activity of strangers. Friends of friends is as far out as +things should ever go. diff --git a/doc/faq.mdwn b/doc/faq.mdwn new file mode 100644 index 0000000..08c7f16 --- /dev/null +++ b/doc/faq.mdwn @@ -0,0 +1,37 @@ +# Plinth and Freedom Plug FAQ + +## General Questions + +### What is the Freedom Plug? + +The Freedom Plug is .... insert links... + +The Freedom Plug is based on the GNU/Debian operating system. It is +not a Linux distribution. It is a network appliance that depends on a +series of Debian packages that configure a plug computer to behave as +a Freedom Plug. + +### What is Plinth? + +Plinth is the web-based GUI administration front end for the Freedom Plug. + +### On what hardware is the Freedom Plug based? + +The current targets are the [Guru Plug](http://guruplug) and the +[Dream Plug](http://dreamplug). + +## Accessing the Freedom Plug + +### Why does ssh listen on port 2222 instead of 22? + +If ssh listens on port 2222, bots and scripts will forever attempt to +guess your username and password. Maybe your password isn't so +strong. Maybe the bots get lucky. Either way, if you allow ssh +access on port 22, you're taking a chance that can be avoided quite +easily by moving your ssh activity to a slightly more obscure port. + +Because ssh activity on these boxes will be limited to programs +configured to work specifically with Freedom Plugs as well relatively +few people generally using ssh, the coordination necessary to use a +non-standard port is easily achieved. + diff --git a/doc/footer.html b/doc/footer.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/footer.html diff --git a/doc/hacking.mdwn b/doc/hacking.mdwn new file mode 100644 index 0000000..38e59fb --- /dev/null +++ b/doc/hacking.mdwn @@ -0,0 +1,97 @@ +# Hacking + +This codebase could really use a testing framework. + +If you are interested in helping out, writing tests is a great place +to start-- you don't need to know much about the code or python to +write useful tests. + +In addition to a testing framework, the code is in need of some +general cleanup. I've been inconsistent in capitalization conventions +as well as in my use of underscores. + +The plugin interface could use some attention as well. Right now, +it's a a bit of a free-for-all until I see how the plugins actually +code up. Channeling all that into a few plugin interface grooves +would be a help. + +If you're feeling more ambitious than that, the best way to improve +Plinth is to add modules. More functionality, especially in the +router section, could convert the Freedom Box from a good idea to a +must-have appliance. + +Beyond that, we need to train some expert eyes on the interaction +between Freedom Boxes. Transparent, zero-config box-to-box backup is +possible. We just need to build an auth and dns layer. There are +lots of theories on how to do this well. The first theory reduced to +practice wins! + +There is a list of TODO items below. Some of them are discrete pieces +that can be tackled without diving too deep into the code. + +## Repository + +Plinth is available from github at +`git://github.com/jvasile/plinth.git`. The [project page on +github](https://github.com/jvasile/plinth) is at +`https://github.com/jvasile/plinth`. + +## Bugs + +There are lots of bugs. We don't have a spec or tests, so a bug is +really just any unexpected behavior. I am not easily surprised, but +there are still lots of bugs. + +<a name="hacking_code_practices" /> + +## Coding Practices + +I try to stick to [PEP 8](http://www.python.org/dev/peps/pep-0008/) +recommendations. That's not to say I don't deviate, just that +deviations are usually bugs that should be fixed. + +### Internationalization + +Every module should `from gettext import gettext as _` and wrap +displayed strings with _(). We don't have the language stuff in place +yet (we have no translation files), but we need to put the +infrastructure in place for it from the start. Use it like this: + + cfg.log.error(_("Couldn't import %s: %s") % (path, e)) + +### Variables and Data Stores + +Plinth needs to keep information for short and long term +future use, and it can't just store all of that on the stack. + +Global config information can be put in the `cfg` module namespace. +Keep it thread and session safe, though, or you'll get undefined +behavior as soon as multiple simultaneous users enter the picture. + +Cherrpy has support for session variables. Use those for short term +user-specific data. + +For long term storage, the Plinth needs a back end +storage solution. Databases are a bit opaque and can be hard for +third party software or shell users to manipulate. For now, I've +decided that persistent data should be placed in dicts and stored in +json format. We'll need a file locking solution too. + +The `user_store.py` module implements the `UserStoreModule` interface +specified in `plugin_mount.py`. Any new system that respects that +interface can be used. The existing `user_store.py` holds entire user +files in memory and caches user files as it goes. This has two +downsides: first, if you have lots of users and store big things in +the user dict, you'll run out of memory. Second, it's not thread +safe. Maybe a database is a good idea after all. + +We do not yet have a means of storing module data for long terms. My +current thinking is that modules can store data in their own +directories. That makes removal easy. + +## Todo + +Plinth has a number of open todo items. Please help! + +* Implement the functions in the submenus of router.py +* Unify our logging and cherrypy's. diff --git a/doc/header.html b/doc/header.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/header.html diff --git a/doc/modules.mdwn b/doc/modules.mdwn new file mode 100644 index 0000000..6d8851a --- /dev/null +++ b/doc/modules.mdwn @@ -0,0 +1,71 @@ +# Modules + +Almost all of the front end's functionality is contained in small, +separate modules that reside in the directory tree beneath +`/modules/installed`. Some are installed by default, some are +required for operation, and some are entirely optional. + +## Installing and Loading Modules + +Eventually, the goal is for module to be separate Debian package so +installation is as simple as `aptitude install freedombox-foo`. As an +intermediate step, we'll start scripting module installation. But +until then, we can install them manually. + +Modules are installed by copying them into the tree beneath the +`/modules/installed` directory. They are activated by linking their +.py files into the modules directory. (Freedom Box will not load +modules unless they are symlinks.) If the module provides other +resources, they can be linked from wherever in the working tree they +need to be. So if a module provides a template, that template can be +linked from `/templates`. + +Modules can be organized into subdirectories beneath the installed +directory. As an initial matter, I've made separate directories for +each major tab. Modules that extend the functionality of the Freedom +Box code base (as opposed to being user-visible interface modules +under specific major tabs) go in `modules/installed/lib`. This +convention can be adjusted or ignored as needed. In fact, modules can +be installed anywhere in the file system as long as the symlinks are +in `/modules`. + +The names of the symlinks in the `modules` directory can be arbitrary, +and if a name is already taken, (e.g. if `router/info.py` and +`apps/info.p`y both exist), append a counter to the end (e.g. link +`info.py` to `router/info.py` and `info1.py` to `apps/info.py`). + +If a module cannot be imported for any reason (e.g. it's a dead +symlink), freedombox.py will log an error but will otherwise just keep +going. + +TODO: automatically prune dead links to clear out old module installs. +This is something the install scripts should do. + +## Pluggable Module Structure + +Plugin interfaces are contained in `plugin_mount.py`. Each interface +is a metaclass. Classes that implement a given interface should +inherit the metaclass and then provide the indicated properties. New +metaclasses can be created to make new classes of plugins. + +Any place that might be affected by arbitrary addition of modules +should have its own metaclass. + +### FromPlugin + +Each form displayed in the interface should inherit from FormPlugin. +This will allow the system to display multiple forms on the page in +the correct order. Forms will likely also want to inherit from +PagePlugin because after the user clicks submit, the plugin usually +displays a responsive message or an error (along with the form to fill +out again). + +## Coding Practices for Modules + +All the +[coding practices for hacking on the front end](#hacking_code_practices) +also apply to modules. In addition, I try to stick to the other +practices listed in this section. + +* Every module should `from gettext import gettext as _` and wrap +displayed strings with _(). diff --git a/doc/roadmap.mdwn b/doc/roadmap.mdwn new file mode 100644 index 0000000..31c00fb --- /dev/null +++ b/doc/roadmap.mdwn @@ -0,0 +1,64 @@ +# Plinth Roadmap + +## 0.1 Basic Wireless Access Point + +* <strike>Basic/Expert mode toggle</strike> +* SSH access +* Connect to WAN via DHCP and static IP +* Offer DHCP on wired and wireless interfaces +* WEP +* WPA2 +* Bridge WAN and LAN + +## 0.2 Basic Wireless Router + +* NAT +* DMZ +* Port forwarding and triggering +* dhcp server (on by default, expert can disable) +* dns server +* MAC address filtering +* NTP client + +## 0.3 Advanced Wireless Router + +* dynamic dns - This is special. See the notes in the section on + [dynamic DNS](#dynamic-dns) for details. +* UPnP +* Cron +* Tx power management + +## 0.4 File Server, More Routing Features + +* boxbackupd +* NFS +* Samba +* Auto mount usb volumes and create samba shares for them +* TOR +* Ad blocking web proxy +* HTTPS everywhere (on by default, experts can disable) + +## 0.5 Print Server and Backups + +* Social backup to friend's boxes via ddns and boxbackup +* printer discovery and sharing via samba and cups + +## 1.0 Plinth Lives! + +* Complete user documentation for every basic and expert menu item +* Package management complete + +## Unscheduled Features + +There are a variety of other features to be implemented, but they are +of lower priority than all the tasks scheduled above. + +* NTP Server +* Provide virtual networks and multiple SSIDs +* Radius Server +* QoS +* file explorer +* Mesh networking +* Chaining boxes so the furthest upstream knows to route messages down + the chain (e.g. if you and your roommate both have your own box, you + just plug one into the other). diff --git a/doc/style.css b/doc/style.css new file mode 120000 index 0000000..1d18299 --- /dev/null +++ b/doc/style.css @@ -0,0 +1 @@ +../static/theme/style.css
\ No newline at end of file diff --git a/doc/themes.mdwn b/doc/themes.mdwn new file mode 100644 index 0000000..3546182 --- /dev/null +++ b/doc/themes.mdwn @@ -0,0 +1,59 @@ +# Themes and Templates + +The visual look and feel of the front end is described in theme files +while <a href="http://cheetahtemplate.org">Cheetah templates</a> +handle layout. + +## Themes + +Themes are stored in `/themes`. Themes consist entirely of static +files (e.g. css, images and javascript) and templates. The default or +active theme is linked from `/static/default` and `templates/default`. +If your theme needs to change anything other than these items, you'll +need a module (perhaps you'll need both). + +There is not currently any support for dynamically choosing a theme at +runtime, but it is theoretically possible. + +## Templates + +Plinth uses the Cheetah templating system. Templates are stored in +`/templates`. Template requirements are not specified. + +TODO: formalize the template spec so template writers know what they need to implement and where they can deviate. + +In this section, I'll attempt to document some of the assumptions the +program has about templates. The goal is that if you write a tempate +that implements the spec, it should work just fine. + +### The Template Stack +The template is a hierarchical stack, where some templates extend on +others. At the base of this stack is `base.tmpl`. It should specify +variables as blocks (rather than using the $ notation). This allows +other templates to easily override the base template. + +Next up is the `page.tmpl`. It extends `base.tmpl` and simply +replaces all the blocks with interpolable variables. Most of the +time, `page.tmpl` is what you will actually use to create pages. + +`err.tmpl` builds on top of `page.tmpl` by adding some decoration to +the title field. + +### Layout + +Plinth expects a `main` block. This is where the +meat of the content goes. It is the center pain in the default +layout. There is a `title` block that the program will fill with text +describing the current page. `sidebar_left` contains the submenu +navigation menu, and `sidebar_right` is where we put all short text +that helps the admin fill out forms. They don't have to be sidebars, +and they don't have to go on the left and right. + +It is possible to override the `footer`, but I haven't yet found a +reason to do so. + +## Cheetah + +This section is for Cheetah hints that are especially useful in this context. + +TODO: add Cheetah hints |