When creating PRs:
- If creating a branch, start it with the author's GitHub username (or another stable branch-safe identifier if unavailable), normalized to lowercase kebab-case, followed by lowercase kebab-case words, with no
/. - Follow the repository PR template when available instead of duplicating it in instructions.
- Do not describe feature flag details such as
isDevFeaturesEnabledin the PR description. - In the
Testingsection, writeTested locally,Unit tests,Integration tests, orN/Aas appropriate. - Do not mention build success, typecheck passing, or other CI-style checks in the
Testingsection.
Changesets and isDevFeaturesEnabled should not appear together for the same change. If a feature is protected by isDevFeaturesEnabled, it is not released yet; add the changeset when removing the feature flag.
Unless explicitly requested otherwise, prefer isDevFeaturesEnabled over a changeset. New features should be protected by isDevFeaturesEnabled by default, unless the affected code is already inside an isDevFeaturesEnabled guard.
When adding a .changeset entry, prefer running pnpm changeset from the repository root.
Changeset text should use this format:
- First paragraph: one concise summary sentence.
- The summary must start with a lowercase letter and omit the trailing period.
- Optional following paragraph: detailed description when needed.
- Write changeset text for the user-facing changelog. Describe the final product capability or behavior users can use.
- Do not describe the release mechanics or implementation process, such as releasing a feature, removing a feature flag, or enabling an already-built feature.
When asked to remove isDevFeaturesEnabled, treat the request as scoped to one specific feature. Never remove all isDevFeaturesEnabled usage globally unless explicitly instructed otherwise.
When adding isDevFeaturesEnabled for a new feature, include a comment that identifies the guarded feature at a high level. Prefer naming the broader feature area rather than a narrow implementation detail so the guard can be found and removed together when the feature is released.
Never bypass commit hooks.
Many packages in this project depend on each other through built artifacts. When checks fail because local package outputs are stale or missing, run pnpm prepack to rebuild them before retrying.
If a hook cannot be fixed safely within the current task, stop and report the blocker with the failed command, relevant output, why it is out of scope, and what decision is needed.
| Service | Port | Purpose |
|---|---|---|
| PostgreSQL | 5432 | Primary data store (required) |
| Logto Core | 3001 (user), 3002 (admin) | Backend: OIDC provider + Management API |
| Console (Vite) | 5002 | Admin dashboard SPA |
| Experience (Vite) | 5001 | Sign-in experience SPA |
- Start Docker daemon:
sudo dockerd &>/tmp/dockerd.log &thensudo chmod 666 /var/run/docker.sock - Start PostgreSQL:
docker run -d --name logto-postgres -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=p0stgr3s -e POSTGRES_DB=logto postgres:17-alpine - Set env:
export DB_URL="postgres://postgres:p0stgr3s@localhost:5432/logto" - If first run (empty database):
pnpm cli db seedandpnpm cli connector link -p . - If Core exits with undeployed alterations on an existing database:
pnpm alteration deploywith the version inpackages/schemas/package.json, thenpnpm alteration deploy next - Start dev:
pnpm start:dev(skippnpm devwhich re-runs prepack; prepack is already done by the update script)
Skills live under .agents/skills/ (Agent Skills format; Cursor Cloud and other agents discover .agents/skills/ and .cursor/skills/). Read a skill only when the task needs it—do not load full command lists into context unless relevant.
| Skill | Read when |
|---|---|
| .agents/skills/logto-dev-environment/SKILL.md | Local dev environment bootstrap, database seed/alteration, service health checks, localhost smoke tests, Experience /demo-app walkthroughs |
| .agents/skills/logto-local-storage/SKILL.md | Avatar/file upload, storage.not_configured, user-assets, collect-profile avatar field |
rsyncmust be installed (needed by@logto/coreforcopy:apidocs). Install withsudo apt-get install -y rsyncif missing.- Connector load errors at startup are expected in dev mode — connectors are not built by default (
pnpm start:devexcludes them). @logto/coreunit tests requirepnpm build:testinpackages/corebefore runningpnpm test:only, because Jest reads from./build(the tsup dev bundle doesn't include test files).@logto/elementstests require Playwright browsers: runpnpm exec playwright install chromium --with-depsinsidepackages/elements.- The pre-commit hook runs
lint-stagedon changed packages; if it fails due to stale outputs, runpnpm prepackfirst. pnpm installrunssync-presetover everypackages/connectors/connector-*directory; orphan connector folders withoutpackage.json(not in git) will break install—remove them beforepnpm install.
- Lint:
pnpm ci:lint(ESLint) andpnpm ci:stylelint(Stylelint) from repo root. - Unit tests:
pnpm ci:testfrom repo root runs all package tests in parallel. - Individual package tests:
pnpm testinside the package directory. - See
.github/CONTRIBUTING.mdfor integration test instructions (Docker-based).