Skip to content

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:

bash
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 recipes

Every 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:

bash
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:seed

After changing a Twig template or a Tailwind design token, rebuild the CSS:

bash
docker compose exec -T app php bin/console tailwind:build

No 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.

bash
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:

bash
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.

  1. Create src/<Module>/{Domain,Application,Infrastructure}/.
  2. Write the Domain interfaces first — entities, value objects, repository interfaces — with no framework imports.
  3. Add the use-cases under Application/Command/<UseCase>/ and any read models under Application/ReadModel/.
  4. Add the HTTP *Action(s) under Application/{Action,Api}/ and import them in config/routes.php (web pages with no prefix; agent endpoints under the /api prefix).
  5. Wire Doctrine + Symfony in Infrastructure/ and register services in config/services.php.
  6. Add the new layer(s) to deptrac.yaml and 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 thresholds or #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:

bash
npm install        # once, in docs/
npm run docs:dev   # local preview with hot reload
npm run docs:build # production build

Diagrams 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.

NetPulse is built and maintained by mrhdolek. Released under the MIT License.