Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
d7e3e540e41474252c1e6314cdd339bc1ff80a82
Author
Runxi Yu <me@runxiyu.org>
Author date
Sun, 30 Mar 2025 21:34:53 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Sun, 30 Mar 2025 22:53:20 +0800
Actions
mandoc in Makefile and forge.5
image: alpine/edge
secrets:
  - cf31947b-6d5d-4229-8ed3-56183102ea81
packages:
  - go
  - mandoc
  - hut
  - golangci-lint
  - linux-headers
  - make
  - gcc
  - musl-dev
tasks:
  - prepare: |
      cd forge
  - build: |
      cd forge
      make
  - lint: |
      cd forge
      golangci-lint run .
  - upload: |
      cd forge
      x="$(git describe --exact || true)"
      if [ -z "$x" ]; then
      	printf 'Not a tag, not uploading artifacts\n' >&2
      else
      	mv forge forge-"$x"-linux-amd64
      	hut git artifact -r forge upload forge-"$x"-linux-amd64
      fi
# SPDX-License-Identifier: AGPL-3.0-only
# SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

.PHONY: clean version.go
.PHONY: clean version.go man

CFLAGS = -Wall -Wextra -Werror -pedantic -std=c99 -D_GNU_SOURCE
MAN_PAGES = forge.5 hookc.1

forge: $(filter-out forge,$(wildcard *)) version.go hookc/*.c hookc/hookc
forge: version.go hookc/*.c hookc/hookc man # TODO
	go mod vendor
	go build .

man: $(MAN_PAGES:%=man/%.html)

man/%.html: man/%
	mandoc -Thtml -O style=static/mandoc.css $< > $@

hookc/hookc:

version.go:
	printf 'package main\n\nconst VERSION = "%s"\n' `git describe --tags --always --dirty` > $@

clean:
	$(RM) forge version.go vendor

.\" SPDX-License-Identifier: AGPL-3.0-only
.\" SPDX-FileContributor: Runxi Yu <https://runxiyu.org>
.Dd March 30, 2025
.Dt HOOKC 1
.Os Lindenii Forge
.Sh NAME
.Nm hookc
.Nd helper binary to delegate Git hook behavior to the forge daemon
.Sh SYNOPSIS
.Nm
.Op Ar argument ...
.Sh DESCRIPTION
.Nm
is a helper binary for Git server-side hooks that relays the hook's context to
a persistent daemon via a UNIX domain socket and communicates back any relevant
responses.
.Pp
It is intended to be invoked by
.Xr git-receive-pack 1
for hooks such as
.Pa pre-receive ,
.Pa update ,
and
.Pa post-receive .
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev LINDENII_FORGE_HOOKS_SOCKET_PATH
Absolute path to the UNIX domain socket on which the daemon is listening.
.It Ev LINDENII_FORGE_HOOKS_COOKIE
64-character authentication cookie used to validate the hook client to the daemon.
.El
.Sh OPERATION
.Nm
collects the following information and sends it to the daemon:
.Bl -bullet
.It
All command-line arguments
.It
All
.Ev GIT_*
environment variables
.It
The raw hook
.Pa stdin
(e.g., old/new ref triplets for
.Pa pre-receive )
.El
.Pp
After sending this data, it waits for a one-byte status code from the daemon,
which becomes
.Nm Ns 's
own exit status.
.Pp
If the daemon sends any output afterward, it is forwarded to standard error
and will appear as
.Dq remote:
output to the user.
.Sh BUGS
.Bl -bullet
.It
The status byte from the daemon currently must be sent before any stderr output.
.It
Currently assumes
.Pa stdin
and
.Pa stderr
are pipes, which is not guaranteed in future versions of Git.
.El
.Sh AUTHORS
.An Runxi Yu Aq Mt https://runxiyu.org
.An Test_User Aq Mt hax@runxiyu.org
.Sh SEE ALSO
.Xr git-receive-pack 1 ,
.Xr forge 1
/*.html
.\" SPDX-License-Identifier: AGPL-3.0-only
.\" SPDX-FileContributor: Runxi Yu <https://runxiyu.org>
.Dd March 30, 2025
.Dt FORGE 5
.Os Lindenii Forge
.Sh NAME
.Nm forge.scfg
.Nd configuration file for Lindenii Forge
.Sh DESCRIPTION
.Nm
describes the configuration for
.Xr forge 1
instance using the
scfg
format.
.Pp
Each directive consists of a name followed by zero or more parameters. Directives may also introduce blocks of subdirectives using braces.
.Pp
Comments begin with
.Sq #
and extend to the end of the line.
.Sh DIRECTIVES
.Bl -tag -width Ds
.It Ic http
Configures the ingress HTTP server.
.Bl -tag -width Ds
.It Ic net
Network type to listen on (e.g., 
.Dq tcp ,
.Dq tcp4 ,
.Dq unix ) .
.It Ic addr
Address to listen on (e.g., 
.Dq :8080
or
.Dq /var/run/lindenii/forge/http.sock ) .
.It Ic cookie_expiry
How long (in seconds) to keep session cookies.
.It Ic root
Canonical root URL of the web interface (e.g.,
.Dq https://forge.example.org ) .
.It Ic read_timeout , write_timeout , idle_timeout
Timeouts, in seconds, for the general HTTP server context.
.It Ic reverse_proxy
Boolean indicating whether to trust X-Forwarded-For headers.
.El
.It Ic ssh
Configures the SSH server.
.Bl -tag -width Ds
.It Ic net
Network type to listen on
.Dq ( tcp
is recommended).
.It Ic addr
Address to listen on (e.g.,
.Dq :22 ) .
.It Ic key
Path to the SSH host key (must be passwordless).
.It Ic root
Canonical SSH URL prefix (e.g.,
.Dq ssh://forge.example.org ) .
.El
.It Ic git
Configures Git repository storage.
.Bl -tag -width Ds
.It Ic repo_dir
Filesystem path under which new repositories are stored.
.El
.It Ic db
Configures database connection.
.Bl -tag -width Ds
.It Ic type
Database type (currently must be
.Dq postgres ) .
.It Ic conn
Connection string, e.g.,
.Dq postgresql:///lindenii-forge?host=/var/run/postgresql .
.El
.It Ic general
Miscellaneous settings.
.Bl -tag -width Ds
.It Ic title
A user-facing name for the instance.
.El
.It Ic hooks
Configures Git hook communication with the forge daemon.
.Bl -tag -width Ds
.It Ic socket
Path to a UNIX domain socket for receiving hook events.
.It Ic execs
Directory where Git hook executables are stored.
.El
.It Ic irc
Optional configuration for IRC presence.
.Bl -tag -width Ds
.It Ic tls
Boolean indicating whether to use TLS.
.It Ic net , addr
Network type and address (e.g.,
.Dq tcp ,
.Dq irc.example.org:6697 ) .
.It Ic sendq
Maximum send queue size.
.It Ic nick , user , gecos
Identity fields for the IRC connection.
.El
.El
.Sh FILES
.Bl -tag -width Ds
.It Pa /etc/lindenii/forge.scfg
Default path to the configuration file.
.El
.Sh SEE ALSO
.Xr forge 1 ,
.Xr hookc 1 ,
.Lk https://git.sr.ht/~emersion/scfg scfg
.Sh AUTHORS
.An Runxi Yu Aq Mt https://runxiyu.org
.An Test_User Aq Mt hax@runxiyu.org
/* $OpenBSD: mandoc.css,v 1.41 2025/01/25 03:17:11 schwarze Exp $ */
/*
 * Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
 *
 * Written by Ingo Schwarze <schwarze@openbsd.org>.
 * I place this file into the public domain.
 * Permission to use, copy, modify, and distribute it for any purpose
 * with or without fee is hereby granted, without any conditions.
 */

/* Global defaults. */

html {		max-width: 65em;
		--bg: #FFFFFF;
		--fg: #000000; }
body {		background: var(--bg);
		color: var(--fg);
		font-family: Helvetica,Arial,sans-serif; }
h1, h2 {	font-size: 110%; }
table {		margin-top: 0em;
		margin-bottom: 0em;
		border-collapse: collapse; }
/* Some browsers set border-color in a browser style for tbody,
 * but not for table, resulting in inconsistent border styling. */
tbody {		border-color: inherit; }
tr {		border-color: inherit; }
td {		vertical-align: top;
		padding-left: 0.2em;
		padding-right: 0.2em;
		border-color: inherit; }
ul, ol, dl {	margin-top: 0em;
		margin-bottom: 0em; }
li, dt {	margin-top: 1em; }
pre {		font-family: inherit; }

.permalink {	border-bottom: thin dotted;
		color: inherit;
		font: inherit;
		text-decoration: inherit; }
* {		clear: both }

/* Search form and search results. */

fieldset {	border: thin solid silver;
		border-radius: 1em;
		text-align: center; }
input[name=expr] {
		width: 25%; }

table.results {	margin-top: 1em;
		margin-left: 2em;
		font-size: smaller; }

/* Header and footer lines. */

div[role=doc-pageheader] {
		display: flex;
		border-bottom: 1px dotted #808080;
		margin-bottom: 1em;
		font-size: smaller; }
.head-ltitle {	flex: 1; }
.head-vol {	flex: 0 1 auto;
		text-align: center; }
.head-rtitle {	flex: 1;
		text-align: right; }

div[role=doc-pagefooter] {
		display: flex;
		justify-content: space-between;
		border-top: 1px dotted #808080;
		margin-top: 1em;
		font-size: smaller; }
.foot-left {	flex: 1; }
.foot-date {	flex: 0 1 auto;
		text-align: center; }
.foot-os {	flex: 1;
		text-align: right; }

/* Sections and paragraphs. */

main {		margin-left: 3.8em; }
.Nd { }
section.Sh { }
h2.Sh {		margin-top: 1.2em;
		margin-bottom: 0.6em;
		margin-left: -3.2em; }
section.Ss { }
h3.Ss {		margin-top: 1.2em;
		margin-bottom: 0.6em;
		margin-left: -1.2em;
		font-size: 105%; }
.Pp {		margin: 0.6em 0em; }
.Sx { }
.Xr { }

/* Displays and lists. */

.Bd { }
.Bd-indent {	margin-left: 3.8em; }

.Bl-bullet {	list-style-type: disc;
		padding-left: 1em; }
.Bl-bullet > li { }
.Bl-dash {	list-style-type: none;
		padding-left: 0em; }
.Bl-dash > li:before {
		content: "\2014  "; }
.Bl-item {	list-style-type: none;
		padding-left: 0em; }
.Bl-item > li { }
.Bl-compact > li {
		margin-top: 0em; }

.Bl-enum {	padding-left: 2em; }
.Bl-enum > li { }
.Bl-compact > li {
		margin-top: 0em; }

.Bl-diag { }
.Bl-diag > dt {
		font-style: normal;
		font-weight: bold; }
.Bl-diag > dd {
		margin-left: 0em; }
.Bl-hang { }
.Bl-hang > dt { }
.Bl-hang > dd {
		margin-left: 5.5em; }
.Bl-inset { }
.Bl-inset > dt { }
.Bl-inset > dd {
		margin-left: 0em; }
.Bl-ohang { }
.Bl-ohang > dt { }
.Bl-ohang > dd {
		margin-left: 0em; }
.Bl-tag {	margin-top: 0.6em;
		margin-left: 5.5em; }
.Bl-tag > dt {
		float: left;
		margin-top: 0em;
		margin-left: -5.5em;
		padding-right: 0.5em;
		vertical-align: top; }
.Bl-tag > dd {
		clear: right;
		column-count: 1;  /* Force block formatting context. */
		width: 100%;
		margin-top: 0em;
		margin-left: 0em;
		margin-bottom: 0.6em;
		vertical-align: top; }
.Bl-compact {	margin-top: 0em; }
.Bl-compact > dd {
		margin-bottom: 0em; }
.Bl-compact > dt {
		margin-top: 0em; }

.Bl-column { }
.Bl-column > tbody > tr { }
.Bl-column > tbody > tr > td {
		margin-top: 1em; }
.Bl-compact > tbody > tr > td {
		margin-top: 0em; }

.Rs {		font-style: normal;
		font-weight: normal; }
.RsA { }
.RsB {		font-style: italic;
		font-weight: normal; }
.RsC { }
.RsD { }
.RsI {		font-style: italic;
		font-weight: normal; }
.RsJ {		font-style: italic;
		font-weight: normal; }
.RsN { }
.RsO { }
.RsP { }
.RsQ { }
.RsR { }
.RsT {		font-style: normal;
		font-weight: normal; }
.RsU { }
.RsV { }

.eqn { }
.tbl td {	vertical-align: middle; }

.HP {		margin-left: 3.8em;
		text-indent: -3.8em; }

/* Semantic markup for command line utilities. */

table.Nm { }
code.Nm {	font-style: normal;
		font-weight: bold;
		font-family: inherit; }
.Fl {		font-style: normal;
		font-weight: bold;
		font-family: inherit; }
.Cm {		font-style: normal;
		font-weight: bold;
		font-family: inherit; }
.Ar {		font-style: italic;
		font-weight: normal; }
.Op {		display: inline flow; }
.Ic {		font-style: normal;
		font-weight: bold;
		font-family: inherit; }
.Ev {		font-style: normal;
		font-weight: normal;
		font-family: monospace; }
.Pa {		font-style: italic;
		font-weight: normal; }

/* Semantic markup for function libraries. */

.Lb { }
code.In {	font-style: normal;
		font-weight: bold;
		font-family: inherit; }
a.In { }
.Fd {		font-style: normal;
		font-weight: bold;
		font-family: inherit; }
.Ft {		font-style: italic;
		font-weight: normal; }
.Fn {		font-style: normal;
		font-weight: bold;
		font-family: inherit; }
.Fa {		font-style: italic;
		font-weight: normal; }
.Vt {		font-style: italic;
		font-weight: normal; }
.Va {		font-style: italic;
		font-weight: normal; }
.Dv {		font-style: normal;
		font-weight: normal;
		font-family: monospace; }
.Er {		font-style: normal;
		font-weight: normal;
		font-family: monospace; }

/* Various semantic markup. */

.An { }
.Lk { }
.Mt { }
.Cd {		font-style: normal;
		font-weight: bold;
		font-family: inherit; }
.Ad {		font-style: italic;
		font-weight: normal; }
.Ms {		font-style: normal;
		font-weight: bold; }
.St { }
.Ux { }

/* Physical markup. */

.Bf {		display: inline flow; }
.No {		font-style: normal;
		font-weight: normal; }
.Em {		font-style: italic;
		font-weight: normal; }
.Sy {		font-style: normal;
		font-weight: bold; }
.Li {		font-style: normal;
		font-weight: normal;
		font-family: monospace; }

/* Tooltip support. */

h2.Sh, h3.Ss {	position: relative; }
.An, .Ar, .Cd, .Cm, .Dv, .Em, .Er, .Ev, .Fa, .Fd, .Fl, .Fn, .Ft,
.Ic, code.In, .Lb, .Lk, .Ms, .Mt, .Nd, code.Nm, .Pa, .Rs,
.St, .Sx, .Sy, .Va, .Vt, .Xr {
		display: inline flow;
		position: relative; }

.An::before {	content: "An"; }
.Ar::before {	content: "Ar"; }
.Cd::before {	content: "Cd"; }
.Cm::before {	content: "Cm"; }
.Dv::before {	content: "Dv"; }
.Em::before {	content: "Em"; }
.Er::before {	content: "Er"; }
.Ev::before {	content: "Ev"; }
.Fa::before {	content: "Fa"; }
.Fd::before {	content: "Fd"; }
.Fl::before {	content: "Fl"; }
.Fn::before {	content: "Fn"; }
.Ft::before {	content: "Ft"; }
.Ic::before {	content: "Ic"; }
code.In::before { content: "In"; }
.Lb::before {	content: "Lb"; }
.Lk::before {	content: "Lk"; }
.Ms::before {	content: "Ms"; }
.Mt::before {	content: "Mt"; }
.Nd::before {	content: "Nd"; }
code.Nm::before { content: "Nm"; }
.Pa::before {	content: "Pa"; }
.Rs::before {	content: "Rs"; }
h2.Sh::before {	content: "Sh"; }
h3.Ss::before {	content: "Ss"; }
.St::before {	content: "St"; }
.Sx::before {	content: "Sx"; }
.Sy::before {	content: "Sy"; }
.Va::before {	content: "Va"; }
.Vt::before {	content: "Vt"; }
.Xr::before {	content: "Xr"; }

.An::before, .Ar::before, .Cd::before, .Cm::before,
.Dv::before, .Em::before, .Er::before, .Ev::before,
.Fa::before, .Fd::before, .Fl::before, .Fn::before, .Ft::before,
.Ic::before, code.In::before, .Lb::before, .Lk::before,
.Ms::before, .Mt::before, .Nd::before, code.Nm::before,
.Pa::before, .Rs::before,
h2.Sh::before, h3.Ss::before, .St::before, .Sx::before, .Sy::before,
.Va::before, .Vt::before, .Xr::before {
		opacity: 0;
		transition: .15s ease opacity;
		pointer-events: none;
		position: absolute;
		bottom: 100%;
		box-shadow: 0 0 .35em var(--fg);
		padding: .15em .25em;
		white-space: nowrap;
		font-family: Helvetica,Arial,sans-serif;
		font-style: normal;
		font-weight: bold;
		background: var(--bg);
		color: var(--fg); }
.An:hover::before, .Ar:hover::before, .Cd:hover::before, .Cm:hover::before,
.Dv:hover::before, .Em:hover::before, .Er:hover::before, .Ev:hover::before,
.Fa:hover::before, .Fd:hover::before, .Fl:hover::before, .Fn:hover::before,
.Ft:hover::before, .Ic:hover::before, code.In:hover::before,
.Lb:hover::before, .Lk:hover::before, .Ms:hover::before, .Mt:hover::before,
.Nd:hover::before, code.Nm:hover::before, .Pa:hover::before,
.Rs:hover::before, h2.Sh:hover::before, h3.Ss:hover::before, .St:hover::before,
.Sx:hover::before, .Sy:hover::before, .Va:hover::before, .Vt:hover::before,
.Xr:hover::before {
		opacity: 1;
		pointer-events: inherit; }

/* Overrides to avoid excessive margins on small devices. */

@media (max-width: 37.5em) {
main {		margin-left: 0.5em; }
h2.Sh, h3.Ss {	margin-left: 0em; }
.Bd-indent {	margin-left: 2em; }
.Bl-hang > dd {
		margin-left: 2em; }
.Bl-tag {	margin-left: 2em; }
.Bl-tag > dt {
		margin-left: -2em; }
.HP {		margin-left: 2em;
		text-indent: -2em; }
}

/* Overrides for a dark color scheme for accessibility. */

@media (prefers-color-scheme: dark) {
html {		--bg: #1E1F21;
		--fg: #EEEFF1; }
:link {		color: #BAD7FF; }
:visited {	color: #F6BAFF; }
}