5.3 KiB
5.3 KiB
Contributing
Thank you for looking at the bastion agent. It runs on machines people own and reads their files, so the bar is: nothing lands that lets personal data leave the machine unsanitized, or lets the agent do more than its signed manifest says.
Before you open a pull request
From the repository root:
python3 -m unittest discover . # every test, including the 3.8 floor
python3 -m pyflakes bastion_agent pii scripts test_syntax_floor.py # pyflakes==4.0.3, as CI runs it
python3 -m bastion_agent.build --out /tmp/agent # the .pyz and its .sha256
python3 /tmp/agent/bastion-agent-*.pyz --version
bash -n bastion_agent/publish.sh pii/verify-live.sh && sh -n bastion_agent/install.sh
- All tests pass. They need PyYAML (the manifest loader uses it), at the
version
build.pypins (PYYAML_VERSION) for the build tests. The round-trip seal tests are skipped whereageandage-keygenare not installed, the TLS tests whereopensslis not, and the release verifier's end-to-end tests wheregitis not, so install them to run the whole suite. CI installs them and fails on any skipped test. - pyflakes finds nothing. No unused import or variable, no undefined name.
- The Python 3.8 floor.
test_syntax_floor.pyparses every module inbastion_agent/andpii/at 3.8 and denies the newer standard-library calls that parse on 3.8 and then fail at run time. The agent ships to whatever the owner runs; keep it there. - The build reproduces.
test_buildbuilds twice and requires identical bytes. Nothing that depends on the machine or the clock (a timestamp, an unsorted directory listing, bytecode, a different PyYAML) may reach the.pyz. Anyone checks a release by rebuilding it (python3 -m bastion_agent.verify_release), so a build that drifts breaks that check for everyone. - Standard library only in both packages, apart from the vendored PyYAML
and the optional
glinerimport. A new dependency is new attack surface in the process that holds the most sensitive data on the machine.
A change to a stage's output adds a test for it, and a change to what a stage refuses adds a test that it refuses.
What a change has to respect
- The agent sends no file and no extracted text to a Host. It keeps the
files, their extracted text and the vault on the machine. The one exception
is
sterilizemode with a server: the PII service atPII_ADDR, or the recall model atLLM_URL, is sent the extracted text, and the PII service keeps its own vault (docs/sanitize.md). What the agent makes for others is the sealed catalog, for the recipients the manifest names, and whatservereturns to a certificate the manifest allows. There is no plaintext sink:sink.plaintextisneverand has no other value. (The bastion's ownreadop is separate: it returns ranges of any file in the granted folder that the grant does not exclude, as they are on disk. From bastion 0.58.1 on it returns nothing in.agent/; an older bastion does unless the grant excludes.agent.) - Sanitize before anything is written for later stages. Chunks, vectors,
keywords, titles, summaries, the catalog and everything
servereturns come from the sanitized store, never the extract store. A test that searches an output for the fixture's personal data should keep passing. - Fail closed. A recall pass that fails stops the stage; a manifest that does not verify runs nothing and serves nothing; a model with the wrong dimension writes nothing. When in doubt, stop and say why in the run record.
- No delete, no write. The agent never changes, moves or deletes the files it reads, and rows in the vault and the vectors table are only ever added.
- Addresses come from the environment, never the manifest. A manifest names models and policy; where the model or the sterilizer runs is the machine's business.
- Stable wire names stay. Installed bastions write
freeagent: 1into every manifest and set theFREEAGENT_*environment variables; renaming one breaks them. Thebuild/freeagent/default output directory keeps its name so output a person already has is not orphaned. - The release keys match the bastion's.
RELEASE_KEYSinbastion_agent/verify_release.pyis a copy ofproductionReleaseKeysin the bastion'sclient-go/release_sign.go. When the bastion's list changes, change this one the same way.
Style
- Comments explain why, in plain words. State the invariant and the failure it prevents. No ticket numbers, no dates, no names of people or machines.
- Documentation describes the program as it is, including what it does not do. It carries no status lines, dates or plans, which go stale the day they land.
- One module per stage, with its own
test_<module>.pyand its own document underdocs/. - Error text a person reads says what was refused and what they can do about it.
Reporting a problem
Bugs and feature requests: the issue tracker on the repository. Security problems: see SECURITY.md, and please do not file them publicly.
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.