Files
privatae-bastion/CONTRIBUTING.md
T
Privatae e4c9b69b46
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s
bastion 0.53.2
2026-10-20 04:42:14 +00:00

3.1 KiB

Contributing

Thank you for looking at the bastion. It is one Go package that ends up on machines people own, so the bar is: nothing lands that widens what a host can do on a machine without the person's grant and, where the design calls for it, their approval on that machine.

What you can do today

Sign-in on this forge is not reachable from the internet, so you cannot file an issue or open a pull request here. You can read, clone, build and verify a release, and fork under the GPL. Report a bug or a security problem by email (see SECURITY.md).

Checks a change has to pass

cd client-go
gofmt -l .            # must print nothing
go vet ./...
CGO_ENABLED=0 go build .
go test ./...

The backup tests also check this code against the age command itself when one is on PATH or named by AGE_BIN; CI installs a pinned age release and sets PRIVATAE_REQUIRE_AGE, so there they fail rather than skip.

The test suite covers all three platforms' parsing and judgement logic from one go test on any OS. Platform-specific decisions live in files with no build tag; only the enumerators are tagged. Keep it that way: a new judgement goes in an untagged file with a table test. Check it with GOOS=windows go vet ./... and GOOS=darwin go vet ./... as well: go build does not compile test files, so only vet sees a test that uses a Linux-only name.

A change to a wall a host's input goes through (a path check, a grant or family check, a signed record's parser) also runs the fuzz targets, and adds one if the wall has none:

deploy/fuzz.sh        # every client-go/fuzz_*_test.go target, 20 seconds each

An input the fuzzer finds is written under client-go/testdata/fuzz/; commit it with the fix, and every later go test runs it.

What a change has to respect

  • The bastion dials out. No new listener on a non-loopback interface. A new listener inside the tunnel is a capability family with a grant check.
  • Fixed argv. A management op runs a fixed command line. A caller value reaches a program only through a strict charset check and never as part of a shell string.
  • No shell in file handlers. Ops that touch the person's files are Go handlers. The one that runs a program takes a tool name from a fixed table.
  • Approval on the machine. Anything that writes to disk, downloads onto it, or opens a root shell is gated by the enrolled second factor. The host never handles the code.
  • Withhold, don't fake. A platform with no implementation for a capability must not advertise it. The host has to be able to tell quiet from blind.
  • Archive, never delete. There is no delete op on shared stores.

Style

  • Comments explain why, in plain words. State the invariant and the failure it prevents. No ticket numbers, no dates, no names.
  • One package, files named by concern. A new concern gets its own file and its own _test.go.
  • Error text that a person will read is a full sentence saying what was refused and what they can do about it.

License

By contributing you agree that your contribution is licensed under the GNU General Public License, version 3 or later, like the rest of the repository.