Implement automatic hook execution after a successful push: when started with
-allow-hooks, objgitd runs .objgit/hooks/receive-pack from the pushed commit
in a restricted shell (kefka virtual bash). The hook sees a read-only view of
the commit tree at /src and writable scratch at /tmp. Execution is async
post-response so hooks cannot reject a push. Only coreutils commands available
(cat, grep, ls, head, tail, sort, sha256sum, etc.) — no arbitrary binaries,
network, or git command.
Refs before/after push are snapshotted to detect branch changes since
transport.ReceivePack does not report them. One hook runs per created/updated
branch; deletions are skipped. Output and exit status logged to slog only.
New packages:
- internal/treefs: lazy read-only billy.Filesystem view of a git tree
(blobs fetched on open, no checkout to disk)
- internal/mountfs: path-prefix composite FS routing /src and /tmp to
separate mounted filesystems
- internal/kefkash: vendored copy of kefka's billysh handler wiring
(adapted to allow writes so /tmp redirections work)
Includes: daemon integration, flags (-allow-hooks, -hook-timeout), tests
(treefs unit tests, diffRefs, e2e push tests), example hook with regression
test, and CLAUDE.md architecture documentation.
Assisted-by: Claude Opus 4.8 via Claude Code
Signed-off-by: Xe Iaso <me@xeiaso.net>
Store POSIX file attributes (uid, gid, mode, mtime) as x-amz-meta-*
headers on S3 objects so POSIX filesystems layered on Tigris can
round-trip ownership and permissions instead of always reporting
mode 0666 with no owner.
The feature is opt-in and off by default: NewS3FS takes functional
options, and without WithUnixMetadata the filesystem behaves exactly
as before. Callers opt in by passing WithUnixMetadata(uid, gid, umask)
and resolve any name strings to numeric IDs themselves.
New unixmeta package implements Encode/Decode and PosixMode/GoFileMode
conversion; the wire format is documented in docs/reference.
Assisted-by: Claude Opus 4.7 via Claude Code
Signed-off-by: Xe Iaso <me@xeiaso.net>
Captures the non-obvious bits: the shared daemon backing both
transports, why git:// receive-pack hides PackfileWriter to avoid
deadlocking on EOF, why s3fs needs a tempBuffer for go-git's
read-while-write PackWriter, and the suffix-based smart-HTTP dispatch.
Assisted-by: Claude Opus 4.7 via Claude Code
Signed-off-by: Xe Iaso <me@xeiaso.net>