shared module. modules/shared/.env.yml holds your environment variables: database URLs, API keys, ports, CORS, and per-environment access lists. modules/shared/src/roles.yml holds the role hierarchy your controllers check for access control.
Both are generated by 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 come scaffolded with defaults that work as-is, and 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, @talosjs/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 @talosjs/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 and registers it in the container. In local development, it also 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, and 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. Whatever you keep, 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. This is 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-commit runs lint-staged, which runs Biome over staged .js/.ts/.jsx/.tsx/.json/.jsonc files. .husky/commit-msg runs bunx commitlint --edit "$1" to validate the message. The rules themselves, along with the interactive bunx commit prompt, live in .commitlintrc.ts.
How to use
Write Conventional Commits in the formtype(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
noExplicitAny and noConsole are set to errors, so the linter rejects any and stray console.* calls. unsafeParameterDecoratorsEnabled is on so the framework’s @inject(...) parameter decorators parse, and imports are organized automatically when you format.
Adjust any rule by editing 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, and most projects never touch them:tsconfig.json
experimentalDecorators and emitDecoratorMetadata are required for the framework’s @decorator.* and @inject decorators. The paths aliases let you import across modules with @module/<module>/... instead of relative paths, so add an entry for each new module to keep its imports resolving. Strict flags like noUncheckedIndexedAccess and noUnusedLocals are 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, with no extra setup.
How to use
Open the project in Zed and you’re done. 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
@talosjs/app-env environment API.Permission component reference
Fine-grained access checks beyond roles.
Create your app
Where these files are generated.