Monotone

Sep 28, 2005 13:55


You may remember that I am looking for a solution to the SNAIL problem, which will have to be based on epistemic monotonic logic. Well, monotone offers half of the solution, monotonic logic. And that is also the more useful half to me, considering that the instantaneous latency of my SNAIL networks is actually low: when my computers are connected to each other, then the communication latency between them is below a few tenths of seconds at most.

monotone is relatively immature, and I had problems with it; moreover, I don't expect it to ever provide the epistemic monotonic logic that I'm longing for. Nevertheless, its basic design seems like the Right Thing to me, the implementation seems sound, the resulting package is already very useful and very easy to get going with, the transition from CVS is remarkably painless, and I expect it to stabilize into something reliable and usable over time. Even in its current state, it is vastly superior to centralized systems such as cvs or its modern replacement subversion; it offers builtin support for importing from a CVS server and for synchronizing with ongoing CVS development. Incidentally, my central CVS server being offline for a week was what prompted me to finally switch. It is also more to my liking than the popular decentralized system darcs, since it maintains databases of everything that is (locally) known about all the code you want, instead of splitting it into as many active per-project, per-branch copies. A monotone checkout is thus very lightweight and very easy to tweak manually, yet robustly managed using cryptographic identification of file contents. File renaming is well-supported, and commits are of course atomic.

Still, I had a few annoyances with the current version of monotone, and I'd like to share my experience with you, so you may avoid a few pitfalls and get more productive.

Firstly, monotone was mightily confused by the entry for "." that it itself created in .mt-attrs when I did a monotone add . -- it was easy to fix this first problem by editing .mt-attrs by hand, but it took me quite some time to understand what was going on; happily, monotone itself could tell it was a bug, so I could choose the right tool to debug, namely strace. Now, I do monotone add $(monotone list unknown) to recursively add files below the top (or current) directory.

Another annoyance is that it insists in using its own network protocol for database synchronization, whereas it is not always possible or affordable to reserve a port for this service, even allegedly cryptographically secure: this involves issues of trust and wasted time by the administrators who manage the firewall and the reliable allocation of ports to servers. I solved this problem with a combination of ssh port redirection and zsh hackery.

# cat zsh.functions.monotone mo_ad () { monotone add $(monotone list unknown) } mo_rm () { \rm $@ ; monotone rm $@ } mo_mv () { \mv $@ ; monotone mv $@ } mo_di () { monotone diff $@ } mo_cm () { monotone commit $@ } mo_diff () { ( cd $1 && shift && mo_di $@ ) | less } mo_commit () {( cd $1 && shift && mo_cm $@ )} mo_up () {( cd $1 && shift && monotone update $@ )} mo_run_server () { coproc ( exec monotone --quiet >& /dev/null \ --db=$DB serve localhost:$PORT "$BRANCHES" ) ; SERVPID=$! echo "Launched a local monotone server with PID=$SERVPID" echo "for database $DB on port $PORT (locally)." mo_kill_server () { echo "Killing monotone server with PID=$SERVPID" kill $SERVPID } if [ -n "$MONOTONE_PASSWORD" ] ; then print -rp -- "$MONOTONE_PASSWORD" fi } mo_run_client () { echo "Connecting to host $HOST to run a monotone client" echo "for database $RDB on port $RPORT (remotely)." stty sane REMOTE_MONOTONE=(monotone --db=$RDB sync localhost:$RPORT $BRANCHES) SSH_ARGS=( -R ${RPORT}:localhost:$PORT $HOST ${(qqq)REMOTE_MONOTONE} # stty sane ; ) if [ -n "${REMOTE_MONOTONE_PASSWORD:=$MONOTONE_PASSWORD}" ] ; then print -r -- "$REMOTE_MONOTONE_PASSWORD" | if command_p cotty 2> /dev/null ; then cotty -1 ssh -t $SSH_ARGS ### Allow for nice output on TTY. else ssh $SSH_ARGS ; fi ### Ugly TTY output. else ssh -t $SSH_ARGS fi } mo_sync () {( DB=$1 HOST=$2 PORT=${3:-35253} RDB=${4:-$DB} RPORT=${5:-$PORT} BRANCHES=${6:-'*'} mo_run_server trap mo_kill_server EXIT # sleep 1 # Give the server some time to breath before startup mo_run_client mo_kill_server trap "echo done." EXIT )}
Note however that said hackery assumes that you configured get_passphrase (see below) on the local end of the mo_sync command. Indeed, monotone's key management is lacking and that makes for not so good compromises between security and usability: not only does it lack an ssh-agent-like system to manage keys (or an interface to ssh-agent itself -- why reinvent incompatible wheels?), it also insists in keeping the secret key in the database itself rather than in a separately manageable secret file. Happily you can customize monotone with a dynamic language; unhappily this is yet another unremarkable crippled language that I had to learn, namely lua; lua hackers on irc.freenode.net were very helpful. Kudos! Here is my ~/etc/monotone/monotonerc. I'm sure you can do better with respect to key management, but it's too much for my expected diminishing returns.

-- -*- lua -*- -- In your ~/.monotone/monotonerc, insert the following: -- dofile(os.getenv("HOME").."/etc/monotone/monotonerc") -- -- You may also insert a definition for your passphrase: -- function get_passphrase(keypair_id) -- return "secret passphrase" -- end function get_netsync_read_permitted (branch, identity) if (identity == "fare@tunes.org") then return true end return false end function get_netsync_write_permitted (identity) if (identity == "fare@tunes.org") then return true end return false end function fare_mkSet(s) local rv = {} for w in string.gfind(s, "%S+") do rv[w] = true end return rv end fare_bad_extensions = fare_mkSet[[ o a la lo so pyc pyo lib fas fasl x86f depend deps out tmp swp cache ps eps pdf aux dvi log bbl blg toc brf haux lot lof idx 4ct 4tc idv lg xref gz bz2 deb tar zip bak orig rej ]] -- gif png jpg txt fare_bad_names = fare_mkSet[[ out.tex init_mzscheme core foo bar baz quux toto tata tutu titi FOO BAR BAZ QUUX TOTO TATA TUTU TITI .cvsignore ]] function ignore_file(name) -- Don't ignore a few precious cached things -- Get it from the WWW -- if (name == "fare/texstuff/fare.eps") then return false end local sname = "/"..name local _, _, basename = string.find(sname, "/([^/]*)$") local _, _, extension = string.find(basename, "%.(%w+)$") if (fare_bad_extensions[extension]) then return true end if (fare_bad_names[basename]) then return true end if (string.find(basename, "%~$")) then return true end if (string.find(basename, "^%.%#")) then return true end if (string.find(sname, "/CVS/")) then return true end if (string.find(sname, "/.svn/")) then return true end if (string.find(sname, "/_darcs/")) then return true end return false end
Finally, monotone being very young, I had to use debian unstable so that I could find recent decent versions of it compatible with the same current netsync protocol version 5 (latest being 0.22, compatibility requiring 0.20 at least) on both my i386 and arm architectures (yes, monotone runs just fine on my good old jornada 820!) There again, debian hackers on irc suggested to RTFM man apt_preferences, and so I now have the following:

# cat /etc/apt/apt.conf APT::Default-Release "testing"; # cat /etc/apt/preferences Package: common-lisp-controller clisp sbcl cl-* monotone user-mode-linux Pin: release a=unstable Pin-Priority: 1000 # apt-get update ; apt-get install monotone
Then, the CGI interface to viewing monotone is a python script that will only work with a recent mod_python in apache 2.0, so I couldn't install it on fare.tunes.org yet. But I will eventually, thanks to user-mode-linux. It is recommended that it should also use its own additional copy of the database for security concerns.

Which brings us to disk usage. monotone, lacking some kind of relevance logic to sort between facts to keep and facts that can be thrown away, practically requires a copy of all versions of all the managed software in every machine. This can be unwieldy on very large multi-gigabyte projects. Also, data seems to be compressed internally, but is then stored as text-encoded binary in the underlying database, which is a weird inefficiency the rationale of which is probably laziness in a time of cheap big disks. If you're a hacker, you may call that room for improvement.

More room for improvement, beside all that was discussed above could include a fact superseding convention in addition to the previously suggested relevance logic (to allow to "erase" sensitive data that you don't want there without having to dump, hack and rebuild the whole database, losing all previous cryptographic certificates), epistemic logic (for efficient synchronization over real SNAIL disconnected networks), a simple protocol for a centralized authority to provide human-readable release numbers (to replace the lacking $Id$ feature, incompatible with general decentralized use), etc. Come on, guys, it's a SMOP!

I hope these tips will help you install monotone and be productive, and maybe convince you to give a try to this fine piece of software (and maybe even hack it).

Update: the new and improved ssh synchronization function (above), now can handle monotone passwords stored in shell variables. Security issues may apply if a malicious user having access to the machine may read these variables by somehow inspecting the memory of your processes; but then again, if someone can do that, crypto isn't the weak link in your security, anyway.

smop, monotone, linux, snail, j820, collaboration, code, versioning, en, hacker

Previous post Next post
Up