Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ PostKit files are split between committed (shared with team) and gitignored (use
- `assertLocalConnection(session, spinner)` (`utils/session.ts`) - Tests local DB connection from session; throws if unreachable
- `resolveApplyTarget(target?)` (`utils/apply-target.ts`) - Resolves `"local"` or `"remote"` apply target; used by infra and seed commands
- `readJsonFile<T>(path)` / `writeJsonFile(path, data)` (`utils/json-file.ts`) - Typed JSON helpers used by remotes and committed migration tracking
- `scaffoldDbInfra()` (`services/scaffold.ts`) - Scaffolds `db/infra/001_roles.sql`+`002_schemas.sql`; runs on both full `postkit init` and `init db`. `scaffoldStorageMigration()` scaffolds the `storage.migrations` bootstrap migration; full `postkit init` only. Both are idempotent (safe to call multiple times, never overwrite)
- `getBlockingPendingCommittedMigrations()` (`utils/committed.ts`) - Like `getPendingCommittedMigrations()` but excludes entries with `blocksSessionStart: false` (e.g. the storage.migrations bootstrap). Used by `db start`'s pending-migrations guard so a brand-new project's first session isn't blocked by a migration nobody has deployed yet; `db deploy`/`db status` still use the unfiltered lookup so it's deployed and listed normally

### Stack Module Architecture

Expand Down Expand Up @@ -197,10 +199,13 @@ The `stack` module manages a local backend service stack (Postgres, Keycloak, Po
- `importRealmTemplate(config, spinner?)` (`services/realm-init.ts`) — Cleans realm JSON and imports via `keycloak-config-cli` container
- `cleanRealmTemplate(raw, realmName)` (`services/realm-init.ts`) — Strips builtins, injects JWT Role Mapper

**`postkit init` scaffold additions:**
**`postkit init [module]` scaffold additions:**
- No `module` argument scaffolds everything (db + auth + stack), unchanged from before scoping existed. Passing `db`, `auth`, or `stack` scaffolds only that module — see `initCommand()`/`initModuleCommand()` in `cli/src/commands/init.ts`
- Scoped runs never re-prompt for or overwrite an existing `postkit.config.json`/`postkit.secrets.json` — they create those files (always with the full db+auth+stack shape, since the db/auth config loaders throw on a missing section) only if missing, and otherwise reuse the existing project name
- Prompts for project name → generates `<name>_<8hexchars>`, stored as `name` in `postkit.config.json`
- Creates `db/infra/001_roles.sql` (anon, authenticated, service_role, app_user, authenticator roles)
- Creates `db/infra/002_schemas.sql` (public, auth, storage schemas)
- Full init only (not `init db`): scaffolds a `storage.migrations` bootstrap migration (`.postkit/db/migrations/00000000000001_create_storage_migrations_table.sql`, tracked in `committed.json`) — a migration-tracking table expected by a self-hosted storage service (e.g. Supabase storage-api) run against the `storage` schema; delete the file + its `committed.json` entry if you don't run one
- Copies vendor provider JARs to `.postkit/auth/providers/`
- Scaffolds realm template at `.postkit/auth/realm/postkit.json`

Expand Down Expand Up @@ -274,6 +279,15 @@ Remotes are managed via utilities in `modules/db/utils/remotes.ts`:
- **tsx** for development mode (direct TS execution without building)
- Output goes to `dist/` with a shebang banner for CLI execution

## Project Init Commands Reference

| Command | Purpose |
|---------|---------|
| `postkit init` | Scaffold the entire project: db + auth + stack |
| `postkit init db` | Scaffold only the db module (`.postkit/db/`, `db/infra/*.sql`) — does not scaffold the `storage.migrations` bootstrap migration, which is full-init only |
| `postkit init auth` | Scaffold only the auth module (`.postkit/auth/`, Keycloak provider sync, realm template) |
| `postkit init stack` | Scaffold only the stack module (`.postkit/stack/`) |

## Database Module Commands Reference

| Command | Purpose |
Expand Down
17 changes: 16 additions & 1 deletion agent/skills/postkit-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ This creates:
- `postkit.config.json` — Committed project configuration (schema paths, flags)
- `postkit.secrets.json` — Gitignored secrets (DB URLs, remote credentials)
- `db/schema/public/` — Default schema directory structure
- `db/infra/` — Infrastructure SQL directory
- `db/infra/` — Infrastructure SQL directory (roles, schemas)
- `.postkit/db/migrations/` — includes a `storage.migrations` bootstrap migration for a self-hosted storage service (e.g. Supabase storage-api) — delete it (and its `committed.json` entry) if you don't run one
- `.postkit/` — Runtime directory (gitignored)
- `.gitignore` entries for secrets and ephemeral files

Expand All @@ -30,6 +31,20 @@ Add `-f` to skip confirmation prompts:
postkit init -f
```

### Scaffold a single module

`postkit init` with no argument scaffolds everything (db + auth + stack). To scaffold only one module — e.g. when adding a module to a project that was only ever partially initialized — pass its name:

```bash
postkit init db # .postkit/db/, db/infra/*.sql
postkit init auth # .postkit/auth/, Keycloak provider sync, realm template
postkit init stack # .postkit/stack/
```

Scoped runs never re-prompt for or overwrite an existing `postkit.config.json`/`postkit.secrets.json` — they only create those files if missing, and reuse the existing project name otherwise. Each run is idempotent and only updates its own slice of `.gitignore`.

Note: the `storage.migrations` bootstrap migration is only scaffolded by the full `postkit init` — `postkit init db` does not create it.

## Configuration Files

PostKit splits config across two files:
Expand Down
4 changes: 4 additions & 0 deletions cli/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ PostKit is a modular CLI toolkit built with **TypeScript** and **Node.js** that
└───────────────────────────────────────────────────────────────────────┘
```

**`postkit init [module]`** scaffolds the project. With no argument it scaffolds everything (db + auth + stack) in one pass — the original, unchanged behavior. Passing `db`, `auth`, or `stack` scopes the run to just that module (`cli/src/commands/init.ts`): scoped runs never re-prompt for or overwrite an existing `postkit.config.json`, only creating it — with the full db+auth+stack shape, since the db/auth config loaders throw on a missing section — if it doesn't exist yet, and reusing the existing project name otherwise.

---

## Module System
Expand Down Expand Up @@ -354,3 +356,5 @@ PostKit files in `.postkit/` are split between gitignored (ephemeral/user-specif
- `.postkit/auth/providers/`
- `.postkit/stack/`
- `postkit.secrets.json`

Full `postkit init` (not the scoped `init db`) also scaffolds a committed bootstrap migration at `.postkit/db/migrations/00000000000001_create_storage_migrations_table.sql`, creating a `storage.migrations` tracking table for a self-hosted storage service (e.g. Supabase storage-api) run against the `storage` schema. It's registered in `committed.json` like any other migration — delete both the file and its `committed.json` entry if you don't run one.
5 changes: 5 additions & 0 deletions cli/docs/db.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,17 @@ PostKit files in `.postkit/db/` are split between gitignored (ephemeral) and com
│ └── 20250131_*.sql
├── committed.json # COMMITTED — migrations tracking index (shared)
└── migrations/ # COMMITTED — committed migrations (for deploy)
├── 00000000000001_create_storage_migrations_table.sql # scaffolded by init — see below
├── 20250130_add_users.sql
└── 20250131_add_posts.sql
```

`postkit init` adds only the ephemeral paths to `.gitignore` (`.postkit/db/session.json`, `.postkit/db/plan.sql`, `.postkit/db/schema.sql`, `.postkit/db/session/`). The `migrations/` directory and `committed.json` are committed to git and shared across the team.

Full `postkit init` (not the scoped `init db`) always scaffolds `00000000000001_create_storage_migrations_table.sql` — a committed migration creating a `storage.migrations` tracking table for a self-hosted storage service (e.g. Supabase storage-api) run against the `storage` schema. If your project doesn't run one, delete the file and its entry in `committed.json`.

This migration is registered with `blocksSessionStart: false`, so it does **not** count toward `db start`'s "pending committed migrations" guard — a brand-new project can run `db start` immediately without deploying it first. `db deploy` and `db status` are unaffected and still treat it as a normal pending migration to deploy/list.

---

## 🚀 Commands
Expand Down
5 changes: 5 additions & 0 deletions cli/docs/e2e-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ Tests infrastructure SQL (roles) and seed data management. Grant permissions are
| `db status` in empty dir | CLI prevents running before init |
| `init --force` | Creates complete project scaffold |
| `init --force` re-init | Force flag allows re-initialization |
| `init bogus --force` | Unknown module name rejected cleanly, no files created |
| `init db --force` | Scopes scaffold to `.postkit/db/`, `db/infra/*.sql` only (no storage.migrations — full-init only) |
| `init auth --force` after `init db` | Adds only auth files; reuses the existing project name |
| `init stack --force` | Scopes scaffold to `.postkit/stack/` only |
| `init db --force` twice | Idempotent — no duplicate committed migrations or gitignore lines |

### Remote Management (`remote-management.test.ts`)

Expand Down
4 changes: 2 additions & 2 deletions cli/docs/stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ Keycloak provider JARs are mounted at `/opt/keycloak/providers` inside the conta

**Destination:** `.postkit/auth/providers/` — gitignored, rebuilt by `postkit init`.

If you add or update a project provider, re-run `postkit init` to sync the new JAR, then restart the stack.
If you add or update a project provider, re-run `postkit init` (or the auth-only `postkit init auth`) to sync the new JAR, then restart the stack.

---

## 🏰 Realm Template + JWT Role Mapper

On the first `stack up` (when `is_initial=true`), PostKit imports a Keycloak realm template.

The template path is configured via `stack.keycloak.realmTemplate` (default: `.postkit/auth/realm/postkit.json`). Scaffolded automatically by `postkit init`.
The template path is configured via `stack.keycloak.realmTemplate` (default: `.postkit/auth/realm/postkit.json`). Scaffolded automatically by `postkit init` (or the auth-only `postkit init auth`).

Before importing, `cleanRealmTemplate()` transforms the raw template JSON:

Expand Down
4 changes: 2 additions & 2 deletions cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@appritech/postkit",
"version": "1.3.1",
"version": "1.3.2",
"description": "PostKit - Developer toolkit for database management and more",
"type": "module",
"main": "dist/index.js",
Expand Down
Loading
Loading