diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..604c82a3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Code of conduct + +## The short version + +Be decent to the people you are working with. Argue with the code, not with the person who wrote it. + +## What is expected + +- **Criticise the work, not its author.** "This drops the lock when the store times out" is a + review. "You clearly did not think about this" is not, and it is also less useful, because it + says nothing about what to change. +- **Assume the other person had a reason.** Most surprising code is a constraint you have not met + yet. Ask what it was before deciding it was carelessness. +- **Take disagreement in good faith.** A maintainer refusing a feature is not rejecting you; this + project says no to things on purpose, and the reason will be given. +- **Respect the time people give.** Reports with the version and the logs, patches with tests, and + answers to review questions are all ways of doing that. + +## What is not acceptable + +Harassment in any form, including sexual attention, slurs, and comments demeaning to someone's +identity or background. Personal attacks. Publishing anyone's private information. Sustained +disruption of discussions, which includes reopening a settled decision for the fifth time. + +None of this depends on whether you meant it as a joke, and none of it becomes acceptable because a +patch is good. + +## Scope + +Issues, pull requests, code review, discussions, and any space where somebody is acting as part of +this project. + +## Reporting + +Email with `CONDUCT` in the subject. Reports are read by the maintainers and +kept confidential, including from the person reported, unless you agree otherwise. If your report +concerns a maintainer, say so and it will be handled by someone else. + +Expect an acknowledgement within 3 working days. The response depends on what happened, from a +private word to a permanent ban, and you will be told what was decided. + +## Attribution + +Adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, +rewritten in this project's own words rather than pasted, because a policy nobody read is a policy +nobody follows. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..1eb7ea81 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,93 @@ +# Contributing + +Bug reports and patches are welcome. This file says what happens to them and what a change needs to +carry before it can be merged, so that nothing about the process is a surprise. + +## Reporting a bug + +Open an [issue](https://github.com/FerrLabs/LFSX/issues). What makes a report actionable, in rough +order of usefulness: + +- The version (`lfsx-server --version`) and how it is deployed: volume or bucket, which auth + provider, behind what proxy. +- What the client did and what it saw. The output of `git lfs push` with `GIT_TRACE=1` and + `GIT_CURL_VERBOSE=1` usually contains the whole story. +- The server's logs around the same moment, at `RUST_LOG=debug` if the default says nothing. +- What you expected instead. Sometimes the disagreement is about the protocol rather than the code, + and that is worth knowing early. + +Never paste a token. The traces above contain the `Authorization` header. + +For anything with a security consequence, read [SECURITY.md](SECURITY.md) instead: those go through +a private advisory rather than an issue. + +## Proposing a change + +Open an issue before a large change. Not as a formality: this server refuses features on purpose, +and it is better to hear that a direction is out of scope before writing it than after. Small fixes +can go straight to a pull request. + +A pull request for anything larger references its issue, and the branch is named for what it does +(`feat/bucket-cache`, `fix/lock-takeover`). + +## What a change has to carry + +**Tests.** A fix carries the test that fails without it. A feature carries the tests for its edge +cases and its error paths, not only the happy one. A test earns its place by failing when the +behaviour it guards breaks: if you cannot say which bug it would catch, it is noise rather than +coverage. + +**A green gate.** `cargo test`, `cargo clippy --all-targets -- -D warnings` and `cargo fmt --check` +all pass. The bucket suite runs against MinIO and Garage in CI, so a storage change is exercised +against two real implementations rather than a stub alone. + +**Documentation, in the same change.** A new environment variable belongs in +[`docs/configuration.md`](docs/configuration.md), a new behaviour in the page that covers it, and a +user-visible change on the marketing site. Docs that trail behind the code are worse than missing +docs, because they are believed. + +**Conventional commits.** `type(scope): description`, in the imperative, one line. The title of a +pull request becomes the commit on `main` and drives the version bump, so `feat:` and `fix:` are +load-bearing. Reserve `!` for a genuine breaking change: a removed flag, a renamed field, a format +that rejects what it used to accept. Adding something is not breaking. + +**Comments that earn their place.** The code here explains itself through names and structure; a +comment is for the reason a reader could not recover from the code, usually why an obvious approach +was rejected. See the existing source for the register. + +## What happens next + +A pull request is reviewed, and the review takes the work seriously enough to disagree with it. +Expect questions about the failure mode you did not mention and the test that would have caught it. +Nothing merges without a green CI and a review. + +## Building it + +[`docs/development.md`](docs/development.md) has the commands, what each suite actually exercises, +and how to run the coverage report and the fuzz targets. + +The one thing that page does not cover is the bucket suite, which needs an S3-compatible store to +point at: + +```bash +docker run -d --name minio -p 9000:9000 \ + -e MINIO_ROOT_USER=lfsxkey -e MINIO_ROOT_PASSWORD=lfsxsecret \ + quay.io/minio/minio server /data + +# once http://127.0.0.1:9000/minio/health/live answers +docker exec minio mc alias set local http://127.0.0.1:9000 lfsxkey lfsxsecret +docker exec minio mc mb --ignore-existing local/lfsx-test + +LFSX_TEST_S3_ENDPOINT=http://127.0.0.1:9000 cargo test --test bucket +``` + +The bucket has to exist before the suite runs: nothing in the server creates one, and +`an_instance_that_cannot_reach_its_bucket_is_not_ready` depends on a missing bucket staying missing. +The name comes from `LFSX_TEST_S3_BUCKET`, which defaults to `lfsx-test`. + +Without `LFSX_TEST_S3_ENDPOINT` those tests skip rather than fail, so a plain `cargo test` stays +useful on a laptop with nothing installed. + +## Licence + +Contributions are licensed under the [MPL-2.0](LICENSE), the licence this project ships under. diff --git a/README.md b/README.md index 207e642a..fde9af18 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,12 @@ Full documentation lives at **[lfsx.dev](https://lfsx.dev)**. The same pages are | [Performance](docs/performance.md) | Measured, not asserted | | [Inspecting a repository](docs/inspecting.md), [Releases](docs/releases.md) | | +## Contributing + +Bug reports and patches are welcome. [`CONTRIBUTING.md`](CONTRIBUTING.md) says what a change has to +carry and what happens to it after that; [`SECURITY.md`](SECURITY.md) covers anything with a +security consequence, which goes through a private advisory rather than an issue. + ## Development ```bash diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..3a9728db --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,55 @@ +# Security policy + +## Reporting a vulnerability + +Report privately through [GitHub Security Advisories](https://github.com/FerrLabs/LFSX/security/advisories/new), +which reaches the maintainers without the report becoming public first. If that page is unavailable +to you, email with `SECURITY` in the subject, which is the address the +[organisation policy](https://github.com/FerrLabs/.github/blob/main/SECURITY.md) names. + +Please do not open a public issue for a vulnerability. An issue is visible to everyone the moment it +exists, including to whoever would use it. + +**What to expect:** an acknowledgement within 3 working days, an assessment of whether it is a +vulnerability and how severe within 10 working days, and a fixed release before any public +disclosure. If a report turns out not to be a vulnerability we will say so and why, rather than +leaving it unanswered. + +Include what you have: the version, whether the deployment uses a volume or a bucket, which auth +provider, and the smallest sequence of requests that shows the problem. A proof of concept against +a server you control is welcome and is not required. + +## What is in scope + +The server and the CLI in this repository, the published container image, and the Helm chart: + +- Serving an object to a caller the forge would refuse, or refusing one it would admit. +- Reading or writing outside the storage root through a crafted object identifier or repository + name. +- Accepting an object whose bytes do not hash to its declared digest. +- Handing out a pre-signed URL that grants more than the batch response was about to grant. +- Two callers being told they hold the same lock. +- Leaking a token, an encryption key or a bucket credential into a log, an error or the pod spec. + +## What is not in scope + +- Anything reachable only with `LFSX_AUTH=disabled`, which accepts every request by design and says + so at boot. That mode is for a trusted network. +- The permissions the forge itself grants. LFSX mirrors them; if a token can push to a repository + upstream, it can push objects for that repository here, and that is the model rather than a bug. +- Denial of service by volume from an authenticated client. There is a transfer cap and a lookup + budget, and the reverse proxy owns request-rate limiting, which + [the documentation says outright](docs/reverse-proxy.md). +- Reports produced by a scanner with no analysis of whether the finding is reachable here. + +## Supported versions + +The latest minor release. This project is young enough that backporting to an older line would +promise more than one maintainer can keep; upgrading is the fix. + +## Verifying what you run + +Every release archive ships a checksum, a build provenance attestation and a cosign signature +bundle, and the container image is signed at push. [`docs/releases.md`](docs/releases.md) has the +commands to check each of them, which is worth doing before you trust a binary that guards your +assets.