Skip to main content
An Talos application is configured through two YAML files in the 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.portPORT, database.urlDATABASE_URL, ai.anthropic.api_keyANTHROPIC_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

Edit modules/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
The 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:
Inside a controller, the same values are available on the context as context.env.

Roles

Roles are an access-control hierarchy declared in modules/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

Edit modules/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
The default scaffold ships a richer hierarchy (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

The allowed_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

Commit messages are linted by a git commit-msg hook so history stays consistent and machine-readable. app:init installs the hook with commitlint:init; re-run that command any time you need to reinstall it. The rules are built into the Talos CLI — there is no config file to maintain, and no Husky or lint-staged. On each commit the hook runs commitlint:check, which validates the message and blocks the commit with a clear error when it does not conform.

How to use

Write Conventional Commits in the form type(scope): Subject. The hook enforces:
  • type — one of build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test, in lower-case.
  • scoperequired and lower-case. It must be common or the name of a module or package in the workspace. Scopes are discovered automatically, so a new module is a valid scope the moment you create it — nothing to register. Multiple scopes are allowed (feat(app, shared): ...).
  • subject — non-empty, starts with an upper-case letter, and has no trailing period. The header must be 100 characters or fewer.
A commit that breaks a rule is rejected with the exact violations, so you can fix the message and commit again.

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:
The generated biome.jsonc enables both the formatter and a strict linter, and turns on import organization:
biome.jsonc
A few defaults worth knowing. Formatting is 2-space indent, 120-column lines, LF endings, and double quotes. 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
A few things to note. 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
This mirrors the bunx biome check --write 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.
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.