shared module:
modules/shared/.env.yml— environment variables: database URLs, API keys, ports, CORS, and per-environment access lists.modules/shared/src/roles.yml— the role hierarchy your controllers check for access control.
app:create and loaded automatically when the app boots. This page covers how to edit them and read their values in code.
Alongside them, app:create writes a set of project-tooling files at the repository root — commit conventions, code style, TypeScript, and editor settings. They are scaffolded with sensible defaults so the toolchain works out of the box; the sections at the end of this page describe each one and how to tune it.
Environment variables
Environment is configured in YAML rather than a flat.env file. At boot, @ooneex/app-env reads modules/shared/.env.yml, flattens its nested keys into uppercase environment variables (app.port → PORT, database.url → DATABASE_URL, ai.anthropic.api_key → ANTHROPIC_API_KEY), and exposes them through a typed, injectable AppEnv class. Empty values are skipped, so unconfigured services stay unset.
Installation
Included in every scaffolded project. To add it manually:How to use
Editmodules/shared/.env.yml. The file is organized into sections — one per concern. Fill in the services you use and leave the rest empty.
modules/shared/.env.yml
app.env value drives environment detection. Supported values include local, development, staging, testing, qa, uat, preview, demo, sandbox, beta, canary, hotfix, and production.
Treat
.env.yml as a secret. Keep real credentials out of version control and
inject them per environment in CI/CD and production.Reading values in code
AppEnv is registered in the container at boot. Inject it into any class with @inject and read its typed properties:
context.env.
Roles
Roles are an access-control hierarchy declared inmodules/shared/src/roles.yml and enforced by @ooneex/role. Each role can inherit from a parent, so a user with ROLE_ADMIN automatically satisfies any route requiring ROLE_USER. A controller restricts access by listing roles on its route — roles: ["ROLE_USER"] — and the framework rejects requests from users who lack a matching (or inheriting) role with 403 Forbidden.
At boot the app validates the file, registers it in the container, and — in local development — regenerates roles.types.ts so role names are type-checked.
Installation
Included in every scaffolded project. To add it manually:How to use
Editmodules/shared/src/roles.yml. The roles section maps friendly names to role keys; the hierarchy section declares each role’s inheritance and description.
modules/shared/src/roles.yml
ROLE_TRIAL_USER, ROLE_VIP_USER, ROLE_REVIEWER, ROLE_MANAGER, ROLE_SYSTEM, …). Keep what you need and prune the rest — but always pick the least-privileged role that satisfies an endpoint.
Restricting a route
List the required role on the controller’s route. Inheritance means higher roles pass automatically:Granting roles by email
Theallowed_users section of .env.yml grants elevated roles to specific emails per environment. When an authenticated user’s email matches, the framework adds the corresponding role at request time — handy for seeding the first ADMIN or SUPER_ADMIN without a database migration:
modules/shared/.env.yml
Commit conventions
Commits are linted with commitlint so history stays consistent and machine-readable.app:init generates .commitlintrc.ts at the project root, wires it into Husky Git hooks, and runs lint-staged on staged files before each commit.
Three pieces work together:
.husky/pre-commitrunslint-staged, which runs Biome over staged.js/.ts/.jsx/.tsx/.json/.jsoncfiles..husky/commit-msgrunsbunx commitlint --edit "$1"to validate the message..commitlintrc.tsdeclares the rules and the interactivebunx commitprompt.
How to use
Write Conventional Commits —type(scope): subject. The config enforces the allowed types and scopes:
.commitlintrc.ts
common, shared, or app. Add a new module’s name to scope-enum when you create it, so commits like feat(billing): add invoices pass. Multiple scopes are allowed (feat(app, shared): ...).
Instead of git commit, you can run the interactive prompt:
Code style
Formatting and linting are handled by Biome — a single fast tool that replaces ESLint and Prettier.app:init writes biome.jsonc at the project root.
How to use
Format and auto-fix the whole project, or lint without writing:biome.jsonc enables both the formatter and a strict linter, and turns on import organization:
biome.jsonc
- 2-space indent, 120-column lines, LF endings, double quotes.
noExplicitAnyandnoConsoleare errors — the linter rejectsanyand strayconsole.*calls.unsafeParameterDecoratorsEnabledis on so the framework’s@inject(...)parameter decorators parse.- Imports are organized automatically on format.
biome.jsonc; see the Biome rules reference for the full list.
TypeScript
tsconfig.json at the project root sets strict compiler options and the module path aliases used across the app.
How to use
The defaults are strict and Bun-targeted — most projects never touch them:tsconfig.json
experimentalDecoratorsandemitDecoratorMetadataare required for the framework’s@decorator.*and@injectdecorators.- The
pathsaliases let you import across modules with@module/<module>/...instead of relative paths. Add an entry for each new module so its imports resolve. - Strict flags like
noUncheckedIndexedAccessandnoUnusedLocalsare on by default — keep them on for the strongest type safety.
Editor (Zed)
app:init writes .zed/settings.json so the Zed editor formats and fixes with Biome on save out of the box.
How to use
Open the project in Zed — no extra setup. The generated settings make Biome the formatter for JS, TS, TSX, JSON, JSONC, CSS, Astro, and Svelte, and run Biome’s fix + organize-imports actions on every save:.zed/settings.json
bun run fmt behavior on save, so files are formatted and imports organized as you work. Using a different editor? Point its Biome integration at biome.jsonc for the same result — VS Code via the Biome extension, or any editor with an LSP client.
Use with Claude and Codex
Initialize the AI skills, then ask your agent to wire configuration in natural language — it edits the YAML files and uses the project’s actual roles.- Claude
- Codex
Prompt
External resources
YAML Spec
The format both configuration files use.
PostgreSQL Connection URLs
Format for the
database.url value.Redis
Cache, queue, and pub/sub connection URLs.
The Twelve-Factor App: Config
Why configuration lives in the environment.
Next steps
Controller
Use the roles you defined to restrict your endpoints.
Auth component reference
The full
@ooneex/app-env environment API.Permission component reference
Fine-grained access checks beyond roles.
Create your app
Where these files are generated.