Thanks for your interest in contributing to Pad! This guide will help you get set up and familiar with how we work.
Pad is built with Pad — we track our own work as Pad items and mirror the newcomer-friendly ones to GitHub. If you're looking for something to pick up, start with these labels:
good first issue— small, self-contained, one focused PR.help wanted— a bit bigger, still scoped to a single PR.
Each issue states the problem, the concrete fix, and pointers to the relevant files. Area labels (area:cli, area:web, area:ci) tell you where the code lives; effort:S / effort:M set expectations.
How PRs are triaged: open a draft PR early and link the issue — a maintainer reviews within a few days. Small, focused PRs merge fastest: one issue, one branch. Comment on an issue to claim it before you start so we don't double up.
Found a problem that isn't an issue yet? Open one describing it (and how to reproduce, for bugs) before writing code, so we can agree on the approach first.
- Go 1.26+
- Node.js 22+
- Make
git clone https://github.com/PerpetualSoftware/pad
cd pad
make build # Build web UI + Go binaryThis produces a ./pad binary in the project root.
make build # Full build: web UI + Go binary
make build-go # Go only (skip web — faster for backend changes)
make dev-web # SvelteKit dev server with hot reload (localhost:5173)
make test # Run Go tests
make lint # Run go vet
make install # Build, install to ~/.local/bin/pad, restart serverTypical workflow:
- Make your changes
make buildto verify everything compilesmake testto run testsmake installto test the full binary locally- Open http://localhost:7777 to verify the web UI
cmd/pad/main.go — CLI entry point (Cobra commands)
internal/
server/ — HTTP API handlers, SSE, middleware
store/ — SQLite CRUD, migrations, FTS
models/ — Go types
items/ — Field validation
collections/ — Default schemas, templates
cli/ — HTTP client, formatting
events/ — EventBus for real-time SSE
config/ — Workspace detection
web/src/
routes/ — SvelteKit pages
lib/api/client.ts — TypeScript API client
lib/types/index.ts — TypeScript types
lib/components/ — Reusable UI components
skills/pad/SKILL.md — Claude Code agent skill
Use descriptive branch names:
feat/relation-field-picker— new featuresfix/dashboard-progress-bar— bug fixesdocs/update-api-reference— documentationrefactor/store-interface— refactoring
Write clear, concise commit messages that explain why, not just what:
Add phase relation field to Tasks collection
Tasks can now be linked to phases via a relation field,
enabling progress tracking on the phase detail page.
- Keep PRs focused — one feature or fix per PR
- Include a description of what changed and why
- Add tests for new backend functionality
- Verify
make buildandmake testpass before opening
- Go: Standard
gofmtformatting. Rungo vet ./...to catch issues. - Svelte: Follow existing component patterns. Use Svelte 5 runes (
$state,$derived,$effect). - TypeScript: Types live in
web/src/lib/types/index.ts.
PR CI runs two security gates that block merging on regressions:
npm run audit:ci(fromweb/;make web-auditfrom the root) —npm audit --audit-level=high --omit=devthroughweb/scripts/ci-audit.mjs, which fails on any HIGH or CRITICAL advisory in production frontend deps and, separately, fails under its own title when the advisory service cannot be reached (after retries) rather than passing. CI runs it after build, svelte-check and vitest so those verdicts exist either way;make checkdoes the same.make vuln(govulncheck -mode binary) — builds the pad binary and scans it for known vulnerabilities in any Go package it actually reaches. Runs in binary mode rather than source mode (govulncheck ./...): source mode builds an SSA call-graph over the whole dependency tree and can consume multiple GB of RAM (BUG-2084), while binary mode reads the compiled binary's symbol table for a fraction of the memory. Pinned to a specific govulncheck version in.github/workflows/ci.yml; bump intentionally rather than tracking@latest.
Both are fast enough to run locally:
make web-audit
make vuln- Add handler in
internal/server/handlers_*.go - Register route in
internal/server/server.go(setupRouter()) - Add store method in
internal/store/if needed - Add CLI client method in
internal/cli/client.go - Add TypeScript type in
web/src/lib/types/index.ts - Add API method in
web/src/lib/api/client.ts
- Add function in
cmd/pad/main.go - Register in
rootCmd.AddCommand()
- Add migration in
internal/store/migrations/ - Update models in
internal/models/ - Migrations run automatically on server start
- Bugs: Use the bug report template — include steps to reproduce
- Features: Use the feature request template — describe the problem first, then your proposed solution
- Questions: Open a discussion or issue
By submitting a pull request, you agree to the terms of our Contributor License Agreement. This is a lightweight CLA that preserves your rights while granting the project maintainers flexibility for future licensing decisions.
Pad is licensed under Apache 2.0. By contributing, your code is released under the same license.