Files
privatae-bastion/CONTRIBUTING.md
T
Privatae 77b7853ae1
ci / personal-data (push) Successful in 6s
ci / fuzz (push) Successful in 2m49s
ci / test (push) Successful in 3m7s
bastion 0.54.1
2026-10-21 23:39:13 +00:00

3.5 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

Issues and pull requests are welcome on GitHub, at github.com/Privatae/bastion. Development happens in a private repository, and the public repository receives the source of each published release as one snapshot commit, so an accepted change is carried into the development repository and arrives with the next release. Sign-in on the git.privatae.ai forge is not reachable from the internet, so issues and pull requests cannot be opened there. You can also read, clone, build and verify a release, and fork under the GPL. Report a security problem by email, not in a public issue (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.