CONTRIBUTING.md
Rama main · réplica de lectura del repositorio original.
# Contributing
The Rabbit Hole exists to make a daily habit less expensive: less time deciding what is worth
reading, more time reading it. If you have an idea that makes it better at that, for you or
for someone else, it is welcome here.
## Talk first, then build
For anything beyond a small fix, open an issue before you write the code. This is to save
your time, not to gate you: it surfaces the design that is already half-decided, or the
reason something was deliberately left out, before you have spent an evening on a patch.
A typo, a broken link, a failing test or an obvious bug fix can come straight as a PR.
There is currently only one maintainer, so feel free to nudge a stale thread.
## Scope
The test for a change is whether it makes someone's daily reading better. Some things are
deliberately outside that:
- **Multi-user.** One reader with local profiles. No per-account profiles or sharing.
- **A hosted service.** Self-hosting is the deployment model.
It is self-hosted. `serve` binds to loopback by default, puts the UI and API behind a single
login, and refuses plain HTTP on any other address. [docs/auth.md](docs/auth.md) covers the
login; [SECURITY.md](SECURITY.md) covers the threat model, and is where to report a
vulnerability rather than in a public issue.
One thing is genuinely undecided: whether this grows into a full self-hosted reader or stays
a focused triage engine. If your idea leans on that answer, say so in the issue. It is a live
question and a good one to argue about.
## Getting set up
```bash
make setup # creates .env from .env.example (optional; the binary reads it)
make sources # adds the example sources from docs/examples/feeds.yaml
make heuristic # a full ingest, no model needed
```
No config file is needed. `make` targets keep their data in `./data/rabbithole.db`, so a checkout
never touches your personal store. `DB=` points them elsewhere, and so does a `RABBITHOLE_DB` in your
shell (`DB=` wins when both are set). `make` does not read `.env`; the binary does. `CONFIG=` names
a file to import on start. Templates for settings, sources and prompts are in `docs/examples/`.
`make heuristic` scores with the keyword scorer instead of an LLM, so it confirms a working
checkout without Ollama/vLLM running. `make help` lists every target.
Go 1.26+ is required.
## Before a PR
```bash
make check # golangci-lint + go test -race + build
```
`make check` runs the same gate CI does, so a green run locally is a green run on the PR. It
needs [golangci-lint](https://golangci-lint.run/welcome/install/); CI pins v2.12.2, so
matching that locally avoids a surprise. The linter also reports formatting, and
`make lint-fix` autofixes most of what it finds, formatting included.
CI also runs on Linux and macOS and enforces 75% total test coverage; `make cover` shows where
you stand. `make bench` runs the ranking and store benchmarks locally.
Add tests for new behaviour, and update the page under `docs/` your change makes wrong.
The suite runs on SQLite and needs nothing installed, which is what `make check` and CI use.
If your change touches `internal/store`, you can run the same tests a second time against
Postgres:
```bash
make pg-up # a throwaway Postgres in podman or docker
make test-pg # the store suite against it
make pg-down # stop it and drop its data
```
This is opt-in locally and never part of `make check`, but CI runs it: the store suite goes
against Postgres 17 on every push, in both exec modes the driver offers, so a split between the
engines fails a build rather than waiting for a reviewer to notice. The container is disposable:
it keeps no volume, and `make pg-down` removes it.
Fork PRs need a maintainer to approve the workflow run before checks start, so expect the
checks to sit idle for a bit on your first contribution.
[docs/architecture.md](docs/architecture.md) has the package map and data flow;
[docs/store.md](docs/store.md) documents the database.
## Commits
`type(scope): imperative summary`, then a line or two on why the change exists.
Releases are cut from `v*` tags. GoReleaser builds the binaries and writes the release notes
from commit subjects, grouped by `feat` and `fix`, so the subject line is what users read.
Sign off your commits with `git commit -s`. That line certifies you wrote the code, or
otherwise have the right to contribute it under the project's license. It is the
[Developer Certificate of Origin](https://developercertificate.org/).
## License
The Rabbit Hole is [Apache-2.0](LICENSE), and contributions come in under the same
terms: anything you deliberately submit for inclusion is licensed Apache-2.0, with no
extra conditions. That is Apache-2.0 section 5, and signing off is how you say it.
You keep the copyright in what you write. There is no CLA and no assignment.
Every Go file opens with two SPDX lines, so keep them on new ones:
```go
// SPDX-FileCopyrightText: 2026 The Rabbit Hole Authors
// SPDX-License-Identifier: Apache-2.0
```
They go at the very top, followed by a blank line, so a package doc comment underneath
still reads as the doc comment. "The Rabbit Hole Authors" is everyone who has
contributed, with the git history as the roster. The year is the project's rather than
the file's, so leave it at 2026 instead of bumping it.