Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 25m ago 4b8c9e1d
history blame raw 1.6 KiB
1package web
2
3// The audit log. Vole records every privileged action in one tab-separated
4// shape, so one file answers "who did this" whether the change arrived over SSH,
5// from the CLI, or from a form. internal/audit owns the shape and the escaping;
6// this file is only the web's way in.
7//
8// A write failure is reported to the server log at error level and nothing else:
9// the action has already happened, and refusing the response would not undo it.
10// The acts that must fail closed — account, key and ACL changes, and a repository
11// deletion — are recorded before they happen by the store they go through
12// (forge/identity, forge/access, forge/repo), so a request that could not be
13// recorded never reaches this file's callers.
14
15import (
16 "rodent.software/vole/internal/audit"
17)
18
19// audit appends one record for an action that has already been taken. A failure
20// is loud, never silent, and never changes the answer the browser already earned.
21func (w *Web) audit(action, actor, subject, detail string) {
22 path := ""
23 if w.Cfg != nil {
24 path = w.Cfg.AuditLog()
25 }
26 if err := audit.Append(path, audit.Record{
27 Action: action,
28 Actor: actor,
29 Subject: subject,
30 Detail: detail,
31 }); err != nil {
32 if w.Logger != nil {
33 w.Logger("audit log write failed: action=%s account=%s subject=%s: %v",
34 action, actor, subject, err)
35 }
36 }
37 // A browser write commits to the metadata branch directly, without a push for the
38 // post-receive hook to observe. Nudging the event loop after every audited write
39 // is cheap: it compares a cursor and does nothing when no metadata changed.
40 if w.MetadataChanged != nil {
41 w.MetadataChanged(subject)
42 }
43}
44