street uses a typed, decorator-driven configuration system. Every config value maps to an environment variable. Sensitive values can be stored encrypted using Vault Mode.
The AppConfig class
src/config/index.ts contains the central configuration class. Each property is decorated with @Config(ENV_VAR_NAME):
import{Injectable}from'../core/container.js';import{Config}from'../core/decorators.js';import{loadConfig}from'../security/vault.js';@Injectable()exportclassAppConfig{@Config('PORT',{required:false})port:string='3000';@Config('PG_HOST',{required:true})pgHost:string='';@Config('JWT_SECRET',{required:true})jwtSecret:string='';@Config('SESSION_KEY',{required:true})sessionKey:string='';// Encrypted field — decrypted at runtime using the KEK@Config('DB_PASSWORD',{encrypted:true,required:true})dbPassword:string='';load(kek?:string):this{returnloadConfig(this,kek);}gethttpPort():number{returnparseInt(this.port,10)||3000;}}
In main.ts:
1
2
3
constconfig=newAppConfig();config.load(process.env['KEK']);// KEK only needed if you use encrypted fieldscontainer.register(AppConfig,config);
All environment variables
Server
Variable
Default
Required
Description
PORT
3000
No
HTTP listen port
HOST
0.0.0.0
No
Bind address
NODE_ENV
development
No
production enables cluster mode
WORKERS
CPU count
No
Number of cluster workers
Database
Variable
Default
Required
Description
PG_HOST
—
Yes
PostgreSQL host
PG_PORT
5432
No
PostgreSQL port
PG_DATABASE
—
Yes
Database name
PG_USER
—
Yes
PostgreSQL user
PG_PASSWORD
—
Yes
PostgreSQL password
Security
Variable
Default
Required
Description
JWT_SECRET
—
Yes
HMAC-SHA256 signing key, min 32 chars
SESSION_KEY
—
Yes
64-char hex string (32 bytes) for AES-256-GCM
KEK
—
Only for Vault Mode
Key-encryption key for secret decryption
Directories
Variable
Default
Required
Description
UPLOADS_DIR
./uploads
No
Multipart upload destination
MIGRATIONS_DIR
./migrations
No
SQL migration files directory
Adding your own config fields
To add a config field, annotate a property with @Config:
loadConfig reads process.env[envKey] for each @Config field. If required: true and the variable is missing, it throws at startup — fail fast, never silently misconfigured.
For high-security deployments, sensitive config values can be stored encrypted. Only the KEK (Key Encryption Key) needs to be provided at runtime — the actual secrets are decrypted in memory and never written to disk.
scrypt to derive a 32-byte key from the KEK + a random salt
AES-256-GCM to encrypt the plaintext value
The encrypted blob format is: [32-byte salt][12-byte IV][16-byte auth tag][ciphertext]
The auth tag provides tamper detection — if the blob is modified in transit or storage, decryption throws immediately. The salt ensures two encryptions of the same secret produce different blobs.
If any required environment variable is missing, config.load() throws before the server starts:
1
2
Error: Missing required environment variable: PG_HOST
at loadConfig (dist/security/vault.js:...)
This is intentional. A misconfigured server that starts silently is worse than one that refuses to start. Fail fast.
Accessing config in services
Inject AppConfig into any service via the constructor:
1
2
3
4
5
6
7
8
9
@Injectable()exportclassEmailService{constructor(privatereadonlyconfig:AppConfig){}asyncsend(to:string,subject:string):Promise<void>{// this.config.smtpPassword is available (decrypted if Vault Mode)console.log(`Sending email to ${to} via SMTP`);}}
The container resolves AppConfig as a singleton — it is constructed and populated once, then shared across all services.