docs(*): add README, LICENSE and versioning
22 files changed, -16/+269
LICENSE
@@ -0,0 +1,21 @@+MIT/X Consortium License++© 2026 lostduk <hi@lostduk.com>++Permission is hereby granted, free of charge, to any person obtaining a+copy of this software and associated documentation files (the "Software"),+to deal in the Software without restriction, including without limitation+the rights to use, copy, modify, merge, publish, distribute, sublicense,+and/or sell copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER+DEALINGS IN THE SOFTWARE.
README
@@ -0,0 +1,33 @@+greenhouse - minimal software forge+===================================+greenhouse is a software forge built from a few independent programs.+It hosts git repositories over ssh and the web, and runs the mailing+lists where patches are sent with git send-email, reviewed and+archived.+++Parts+-----+ hedge git access control for ssh+ petal git web interface (CGI)+ pollen mailing list manager (Postfix pipe transport)+ herbarium mailing list archive web interface (CGI)+++Overview+--------+ git push, fetch -> sshd -> hedge -> git-shell -> /srv/git+ browser -> httpd -> petal -> /srv/git+ mail -> Postfix -> pollen -> sendmail -> subscribers+ pollen -> /srv/lists+ browser, b4 -> httpd -> herbarium -> /srv/lists+++Installation+------------+Each part is built and installed on its own:++ cd petal+ make clean install++See the README and man page in each directory.
hedge/Makefile
@@ -24,7 +24,7 @@ install: all cp -f hedge ${DESTDIR}${PREFIX}/bin chmod 755 ${DESTDIR}${PREFIX}/bin/hedge mkdir -p ${DESTDIR}${MANPREFIX}/man1- cp -f hedge.1 ${DESTDIR}${MANPREFIX}/man1+ sed "s/VERSION/${VERSION}/g" < hedge.1 > ${DESTDIR}${MANPREFIX}/man1/hedge.1 chmod 644 ${DESTDIR}${MANPREFIX}/man1/hedge.1 uninstall:
hedge/README
@@ -0,0 +1,39 @@+hedge - git access control for ssh+==================================+hedge is run as the forced command of an ssh key. It allows git fetch+and push to the repositories an access list grants to that key, and+nothing else.+++Requirements+------------+In order to run hedge you need git-shell, which comes with git.+++Installation+------------+Edit config.mk to match your local setup (hedge is installed into+the /usr/local namespace by default).++Afterwards enter the following command to build and install hedge (if+necessary as root):++ make clean install+++Running hedge+-------------+List the grants in the acl file:++ greenhouse write alice+ greenhouse read bob++and give each ssh key of the git user its identity:++ command="/usr/local/bin/hedge alice",restrict ssh-ed25519 AAAA...+++Configuration+-------------+The configuration of hedge is done by creating a custom config.h+and (re)compiling the source code.
hedge/config.mk
@@ -1,7 +1,9 @@+VERSION = 0.1+ PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man -CPPFLAGS = -D_XOPEN_SOURCE=700+CPPFLAGS = -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${CPPFLAGS} LDFLAGS =
hedge/hedge.1
@@ -1,9 +1,12 @@-.TH HEDGE 1 hedge+.TH HEDGE 1 hedge\-VERSION .SH NAME hedge \- git access control for ssh .SH SYNOPSIS .B hedge .I id+.br+.B hedge+.B \-v .SH DESCRIPTION .B hedge is run as the forced command of an ssh key and lets the identity@@ -37,6 +40,10 @@ segments matching so nested repositories such as .B group/repo are allowed.+.SH OPTIONS+.TP+.B \-v+prints version information to stderr, then exits. .SH FILES .TP .B acl
hedge/hedge.c
@@ -108,7 +108,7 @@ trim(char *path) void usage(void) {- fputs("usage: hedge id\n", stderr);+ fputs("usage: hedge -v | id\n", stderr); exit(1); } @@ -146,6 +146,10 @@ main(int argc, char *argv[]) char *path; size_t i; + if (argc == 2 && !strcmp(argv[1], "-v")) {+ fputs("hedge-"VERSION"\n", stderr);+ return 1;+ } if (argc != 2) usage(); id = argv[1];
herbarium/Makefile
@@ -28,7 +28,7 @@ install: all chmod 644 ${DESTDIR}${PREFIX}/share/herbarium/style.css\ ${DESTDIR}${PREFIX}/share/herbarium/icon.svg mkdir -p ${DESTDIR}${MANPREFIX}/man1- cp -f herbarium.1 ${DESTDIR}${MANPREFIX}/man1+ sed "s/VERSION/${VERSION}/g" < herbarium.1 > ${DESTDIR}${MANPREFIX}/man1/herbarium.1 chmod 644 ${DESTDIR}${MANPREFIX}/man1/herbarium.1 uninstall:
herbarium/README
@@ -0,0 +1,39 @@+herbarium - mailing list archive web interface+==============================================+herbarium is a CGI program that shows the archives kept by pollen as+threads, with raw messages and thread mboxes for b4 and git am.+++Requirements+------------+In order to build herbarium you need a C library with iconv.+++Installation+------------+Edit config.mk to match your local setup (herbarium is installed into+the /usr/local namespace by default).++Afterwards enter the following command to build and install herbarium+(if necessary as root):++ make clean install+++Running herbarium+-----------------+Run herbarium as a CGI program, for example with nginx and fcgiwrap,+as a user that can read the list archives, and serve style.css and+icon.svg from share/herbarium. Only lists that contain a public file+are shown. See herbarium(1) for an example configuration.++To fetch a thread with b4:++ git config b4.midmask https://lists.example.org/list/%s+ b4 am <message-id>+++Configuration+-------------+The configuration of herbarium is done by creating a custom config.h+and (re)compiling the source code.
herbarium/config.mk
@@ -1,7 +1,9 @@+VERSION = 0.1+ PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man -CPPFLAGS = -D_XOPEN_SOURCE=700+CPPFLAGS = -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${CPPFLAGS} LDFLAGS =
herbarium/herbarium.1
@@ -1,8 +1,9 @@-.TH HERBARIUM 1 herbarium+.TH HERBARIUM 1 herbarium\-VERSION .SH NAME herbarium \- mailing list archive web interface .SH SYNOPSIS .B herbarium+.RB [ \-v ] .SH DESCRIPTION .B herbarium is a CGI program that shows the archives@@ -72,6 +73,10 @@ The whole thread as a gzipped mboxrd file, as fetched by .P Every response carries a Content-Security-Policy that allows only the stylesheet and icon from the same origin.+.SH OPTIONS+.TP+.B \-v+prints version information to stderr, then exits. .SH ENVIRONMENT .TP .B PATH_INFO
herbarium/herbarium.c
@@ -2015,11 +2015,15 @@ xstrndup(const char *s, size_t n) /* main */ int-main(void)+main(int argc, char *argv[]) { char path[2048]; const char *q; + if (argc == 2 && !strcmp(argv[1], "-v")) {+ fputs("herbarium-"VERSION"\n", stderr);+ return 1;+ } base = envor("SCRIPT_NAME", ""); q = envor("QUERY_STRING", ""); if (!strncmp(q, "ofs=", 4) && (ofs = strtol(q + 4, NULL, 10)) < 0)
petal/Makefile
@@ -28,7 +28,7 @@ install: all chmod 644 ${DESTDIR}${PREFIX}/share/petal/style.css\ ${DESTDIR}${PREFIX}/share/petal/icon.svg mkdir -p ${DESTDIR}${MANPREFIX}/man1- cp -f petal.1 ${DESTDIR}${MANPREFIX}/man1+ sed "s/VERSION/${VERSION}/g" < petal.1 > ${DESTDIR}${MANPREFIX}/man1/petal.1 chmod 644 ${DESTDIR}${MANPREFIX}/man1/petal.1 uninstall:
petal/README
@@ -0,0 +1,34 @@+petal - git web interface+=========================+petal is a CGI program that shows bare git repositories: an index,+log, tree, raw files, commits with diffs, and refs.+++Requirements+------------+In order to build petal you need the libgit2 header files.+++Installation+------------+Edit config.mk to match your local setup (petal is installed into+the /usr/local namespace by default).++Afterwards enter the following command to build and install petal (if+necessary as root):++ make clean install+++Running petal+-------------+Run petal as a CGI program, for example with nginx and fcgiwrap, and+serve style.css and icon.svg from share/petal. Only repositories that+contain git-daemon-export-ok are shown. See petal(1) for an example+configuration.+++Configuration+-------------+The configuration of petal is done by creating a custom config.h+and (re)compiling the source code.
petal/config.mk
@@ -1,3 +1,5 @@+VERSION = 0.1+ PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man @@ -7,7 +9,7 @@ GIT2LIB = /usr/local/lib INCS = -I${GIT2INC} LIBS = -L${GIT2LIB} -lgit2 -CPPFLAGS = -D_XOPEN_SOURCE=700+CPPFLAGS = -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS}
petal/petal.1
@@ -1,8 +1,9 @@-.TH PETAL 1 petal+.TH PETAL 1 petal\-VERSION .SH NAME petal \- git web interface .SH SYNOPSIS .B petal+.RB [ \-v ] .SH DESCRIPTION .B petal is a CGI program that shows the bare git repositories in@@ -67,6 +68,10 @@ Branches and tags. .P Every response carries a Content-Security-Policy that allows only the stylesheet and icon from the same origin.+.SH OPTIONS+.TP+.B \-v+prints version information to stderr, then exits. .SH ENVIRONMENT .TP .B PATH_INFO
petal/petal.c
@@ -1145,11 +1145,15 @@ xmlencoden(const char *s, size_t len) /* main */ int-main(void)+main(int argc, char *argv[]) { char path[1024]; const char *q; + if (argc == 2 && !strcmp(argv[1], "-v")) {+ fputs("petal-"VERSION"\n", stderr);+ return 1;+ } base = envor("SCRIPT_NAME", ""); q = envor("QUERY_STRING", ""); if (!strncmp(q, "ofs=", 4) && (ofs = strtol(q + 4, NULL, 10)) < 0)
pollen/Makefile
@@ -24,7 +24,7 @@ install: all cp -f pollen ${DESTDIR}${PREFIX}/bin chmod 755 ${DESTDIR}${PREFIX}/bin/pollen mkdir -p ${DESTDIR}${MANPREFIX}/man1- cp -f pollen.1 ${DESTDIR}${MANPREFIX}/man1+ sed "s/VERSION/${VERSION}/g" < pollen.1 > ${DESTDIR}${MANPREFIX}/man1/pollen.1 chmod 644 ${DESTDIR}${MANPREFIX}/man1/pollen.1 uninstall:
pollen/README
@@ -0,0 +1,39 @@+pollen - mailing list manager+=============================+pollen delivers posts to the subscribers of a mailing list, keeps an+archive, and handles subscribe, unsubscribe and help requests sent by+mail.+++Requirements+------------+In order to run pollen you need Postfix, or another mail server that+can pipe a message to a program, and a sendmail command.+++Installation+------------+Edit config.mk to match your local setup (pollen is installed into+the /usr/local namespace by default).++Afterwards enter the following command to build and install pollen (if+necessary as root):++ make clean install+++Running pollen+--------------+Add pollen as a pipe transport for the list domain in master.cf:++ pollen unix - n n - - pipe+ flags=Rq user=pollen argv=/usr/local/bin/pollen ${user} ${extension}++and create a directory per list. See pollen(1) for the list files and+the rest of the Postfix configuration.+++Configuration+-------------+The configuration of pollen is done by creating a custom config.h+and (re)compiling the source code.
pollen/config.mk
@@ -1,7 +1,9 @@+VERSION = 0.1+ PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man -CPPFLAGS = -D_XOPEN_SOURCE=700+CPPFLAGS = -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${CPPFLAGS} LDFLAGS =
pollen/pollen.1
@@ -1,10 +1,13 @@-.TH POLLEN 1 pollen+.TH POLLEN 1 pollen\-VERSION .SH NAME pollen \- mailing list manager .SH SYNOPSIS .B pollen .I list .RI [ extension ]+.br+.B pollen+.B \-v .SH DESCRIPTION .B pollen delivers one message, read from standard input, to the mailing list@@ -55,6 +58,10 @@ in a single call for all subscribers, with as envelope sender. If that call fails, the archived copy is removed again so Postfix can retry cleanly.+.SH OPTIONS+.TP+.B \-v+prints version information to stderr, then exits. .SH COMMANDS Commands are addressed as .IB list + command @ domain .
pollen/pollen.c
@@ -913,7 +913,7 @@ end: void usage(void) {- fputs("usage: pollen list [extension]\n", stderr);+ fputs("usage: pollen -v | list [extension]\n", stderr); exit(EX_USAGE); } @@ -942,6 +942,10 @@ main(int argc, char *argv[]) char ext[256]; struct stat st; + if (argc == 2 && !strcmp(argv[1], "-v")) {+ fputs("pollen-"VERSION"\n", stderr);+ return 1;+ } umask(027); signal(SIGPIPE, SIG_IGN); openlog("pollen", LOG_PID, LOG_MAIL);