Contributing
Thanks for helping improve NetPulse — this page covers setting up a dev environment, the quality gate every change must pass, and how to open a pull request. For the big picture, read the architecture overview; to get the stack running, start with getting started.
Local development
NetPulse runs in Docker. The task runner is just (brew install just, or use the Nix shell which provides it). Bring the stack up once and develop against it:
just install # build the image, start the stack, run composer install
just start # docker compose up -d (start an already-built stack)
just stop # docker compose down --remove-orphans
just bash # open a shell inside the app container
just # list all recipesEvery php, composer, and bin/console command runs inside the app container — never on the host. Use a shell (just bash) or the one-off form:
docker compose exec -T app composer install
docker compose exec -T app composer migrate # build/upgrade the DEV database from migrations
docker compose exec -T app php bin/console app:dev:seedAfter changing a Twig template or a Tailwind design token, rebuild the CSS:
docker compose exec -T app php bin/console tailwind:buildNo Node for the app
The frontend is built with Symfony AssetMapper — JS is served from the import map and there is no npm/Vite/Webpack step. The only tailwind:build is the CSS rebuild above.
Nix shell
Prefer the host? A classic shell.nix (no flake, no direnv) provides PHP 8.5, Composer, Mago, the Symfony CLI, and just — enough for the quality gate and CLI work without Docker. The full app stack (nginx, the agent + Ookla speedtest CLI, Prometheus, Grafana) still runs in Docker Compose.
nix-shell # enter the dev shell (PHP 8.5 + Composer + Mago + Symfony CLI + just)
nix-shell --pure # full isolation from the host
nix-shell --arg php-version 8.4 # pick a PHP version (default: 8.5)
nix-shell --arg with-xdebug true # add Xdebug
type php # show the Nix PHP binary path (for IDE config)Mago and Deptrac install as isolated Composer tools (tools/mago/, tools/deptrac/) via the composer install tools:install hook. Optional custom prompt: copy .nix/shell/starship.toml.dist to .nix/shell/starship.toml.
The quality gate
Before you push, this checklist must be green. CI runs the same job — Test&lint PHP codebase — on every push and pull request to main, so getting it green locally means getting it green in CI. Run it inside the Nix shell or the app container; the just recipes wrap the same commands:
just lint # Mago format --check + Mago lint
just analyze # Mago static analysis (src + config; must be clean)
just deptrac # architecture, 0 violations
composer db:test # apply migrations to the TEST database
composer test # PHPUnit
just fix # auto-format + auto-fix (Mago)Mago is the sole linter, formatter, and static analyzer — it replaced PHP-CS-Fixer and PHPStan. Mago and Deptrac are isolated Composer tools under tools/. mago analyze covers src + config (matching the previous PHPStan scope) and must be clean — there is no baseline; two checks that go beyond that standard (mixed-assignment, avoid-catching-error) are intentionally not enforced (see mago.toml). In CI, mago lint/analyze findings are uploaded to GitHub Code scanning (Security tab + inline annotations on the PR diff).
Behat is parked
The Behat suite is paused until its ecosystem supports Symfony 8 (behat/behat caps Symfony at ^7). The .feature files and behat.yml stay in the repo; coverage currently rests on the PHPUnit integration tests.
Run the gate sequentially
Run the gate steps one at a time (in nix-shell or via just bash). Don't background or overlap container commands.
Adding a module
Modules live under src/<Module>/ and each splits into Domain, Application, and Infrastructure.
- Create
src/<Module>/{Domain,Application,Infrastructure}/. - Write the Domain interfaces first — entities, value objects, repository interfaces — with no framework imports.
- Add the use-cases under
Application/Command/<UseCase>/and any read models underApplication/ReadModel/. - Add the HTTP
*Action(s) underApplication/{Action,Api}/and import them inconfig/routes.php(web pages with no prefix; agent endpoints under the/apiprefix). - Wire Doctrine + Symfony in
Infrastructure/and register services inconfig/services.php. - Add the new layer(s) to
deptrac.yamland keep deptrac at 0 violations.
Pull requests
- Branch off
main. - Keep the quality gate green — CI will reject anything that isn't.
- PR titles must start with
-(a dash and a space), optionally preceded by an issue reference — e.g.- Add per-connection alert thresholdsor#42 - Fix the ping unit on the heatmap. A CI check enforces the pattern^(#\d+ )?- .+. - Fill in the PR template, and keep each change focused on a single concern.
- Reporting a bug or proposing a feature? Use the repository's issue templates so we capture the details we need.
Keep the diff reviewable
Large, mixed-concern PRs are hard to review and slow to land. Split unrelated changes into separate branches and PRs.
Documentation
This site lives in docs/ and is built with VitePress. Documentation tooling does use Node — run it from the docs/ directory:
npm install # once, in docs/
npm run docs:dev # local preview with hot reload
npm run docs:build # production buildDiagrams are authored as Mermaid fenced code blocks. Edit the relevant page, preview it with docs:dev, and include doc updates in the same PR as the code they describe.