Tina4

Chapter 4: Environment Variables#

Every piece of Tina4 configuration lives in one file. A .env at the root of your project. All optional. All with sensible defaults. Identical across Python, PHP, Ruby, and Node.js. This chapter is the complete reference.

How .env Files Work#

A .env file is plain text. Key-value pairs. Nothing more.

bash
# This is a commentTINA4_DATABASE_URL=sqlite:///data/app.dbTINA4_DEBUG=trueโ€‹# Blank lines are ignoredโ€‹# Values with spaces need quotesTINA4_MAIL_FROM="My App <noreply@example.com>"โ€‹# No quotes needed for simple valuesTINA4_SECRET=my-secret-key-change-in-production

Rules#

Six rules govern the format:

  1. One variable per line.
  2. Format is KEY=VALUE -- no spaces around the =.
  3. Lines starting with # are comments.
  4. Blank lines are ignored.
  5. Values can be wrapped in double quotes ("value") or single quotes ('value').
  6. No variable interpolation -- $OTHER_VAR is treated as a literal string.

Simple format. No surprises. A developer who has never seen a .env file understands it in thirty seconds.

Every Framework Variable Needs the TINA4_ Prefix#

Tina4 3.12 renamed every framework variable to carry the TINA4_ prefix. The pre-3.12 names (SECRET, DATABASE_URL, SWAGGER_TITLE, SMTP_HOST, HOST_NAME, and the rest) no longer work. Setting one at startup does not quietly fall back to a default. The framework refuses to boot and prints each old name beside its new one, so a stale .env fails loud instead of running auth or the database on the wrong config.

Run tina4 env --migrate to rewrite an existing .env automatically, or rename by hand. During a migration window, set TINA4_ALLOW_LEGACY_ENV=true to bypass the guard while both names are present.

Runtime conventions stay un-prefixed on purpose. PORT, HOST, NODE_ENV, RACK_ENV, RUBY_ENV, and ENVIRONMENT are platform names, not Tina4 config, so the guard leaves them alone.

The .env File Is Not Committed to Git#

The .env file holds secrets. Database passwords. JWT keys. API tokens. It belongs in .gitignore. When you run tina4 init, the generated .gitignore already excludes it.

Commit a .env.example instead. Placeholder values. A map for the next developer:

bash
# .env.example -- copy to .env and fill in real valuesTINA4_DATABASE_URL=sqlite:///data/app.dbTINA4_DEBUG=falseTINA4_SECRET=CHANGE_METINA4_MAIL_HOST=smtp.example.comTINA4_MAIL_USERNAME=TINA4_MAIL_PASSWORD=

The example file documents what the application expects. The real file stays on the machine that runs it. Never in the repository.

The Priority Chain#

Tina4 resolves every configurable value through a three-level chain:

Constructor argument  >  .env file  >  Hardcoded default

Three levels. Strict order. No exceptions.

  1. Constructor argument wins. Pass a value in code and it overrides everything.
  2. .env file is second. No code override? The .env value takes over.
  3. Default is last. Neither code nor .env specifies a value? The framework's built-in default applies.

This pattern holds across all variables and all four language implementations. Learn it once. Apply it everywhere.

Boolean Values#

Environment variables are strings. The .env file has no concept of true or false. Tina4 recognises these values as truthy:

ValueTreated as
true, True, TRUEtrue
1true
yes, Yes, YEStrue
on, On, ONtrue

Everything else is false:

ValueTreated as
false, 0, no, offfalse
(empty string)false
(variable not set)false

Write whichever style your team prefers:

bash
# All of these enable debug modeTINA4_DEBUG=trueTINA4_DEBUG=1TINA4_DEBUG=yes

Complete .env Reference#

Server#

VariableDefaultDescription
HOST0.0.0.0Bind address. 0.0.0.0 listens on all interfaces (required for Docker). 127.0.0.1 restricts to localhost.
PORTSee belowHTTP server port. Each framework has a unique default to avoid conflicts when running side-by-side.
TINA4_DEBUGfalseMaster toggle. Enables debug overlay, full stack traces, Swagger UI, live reload, query logging. Never true in production.

Default ports by framework:

FrameworkDefault Port
PHP7145
Python7146
Ruby7147
Node.js7148

Authentication#

VariableDefaultDescription
TINA4_SECRETtina4-default-secretSecret key for JWT signing (HMAC-SHA256). Long, random, never committed to git. Change this in production.
TINA4_API_KEY(none)Static API key for bearer token authentication. When set, requests with Authorization: Bearer {TINA4_API_KEY} are accepted.
TINA4_TOKEN_LIMIT60Token lifetime in minutes. Tokens issued by get_token() / getToken() expire after this many minutes.

CSRF Protection#

VariableDefaultDescription
TINA4_CSRFtrueEnable CSRF token validation on POST/PUT/PATCH/DELETE. Set to false to disable (e.g. for internal microservices behind a firewall).

CSRF is on by default. When enabled:

  • POST/PUT/PATCH/DELETE requests must include a formToken in the request body or an X-Form-Token header.
  • GET/HEAD/OPTIONS requests are not checked.
  • Requests with a valid Authorization: Bearer token skip CSRF validation.
  • Routes marked @noauth() skip CSRF validation.
  • Tokens in query strings are rejected (security risk).

To disable for internal services:

bash
TINA4_CSRF=false

Database#

VariableDefaultDescription
TINA4_DATABASE_URLsqlite:///data/app.dbConnection string. The URL scheme selects the driver.
TINA4_DATABASE_USERNAME(from URL)Override the username in TINA4_DATABASE_URL. Useful when credentials contain special characters.
TINA4_DATABASE_PASSWORD(from URL)Override the password in TINA4_DATABASE_URL.
TINA4_DATABASE_CHARSETUTF8Firebird connection charset. Set it when a legacy database created under NONE returns double-encoded UTF-8. A ?charset= query on the connection URL takes precedence.
TINA4_AUTOCOMMITtrueStandalone writes auto-commit on their own connection (durable + visible across a pool); explicit transactions stay atomic. Set false for strict manual-commit mode.

Connection string formats:

bash
# SQLite (default -- no credentials needed)TINA4_DATABASE_URL=sqlite:///data/app.dbโ€‹# PostgreSQLTINA4_DATABASE_URL=postgresql://user:password@hostname:5432/database_nameโ€‹# MySQL / MariaDBTINA4_DATABASE_URL=mysql://user:password@hostname:3306/database_nameโ€‹# Microsoft SQL ServerTINA4_DATABASE_URL=mssql://user:password@hostname:1433/database_nameโ€‹# Firebird (charset defaults to UTF8; add ?charset=NONE for a legacy NONE database)TINA4_DATABASE_URL=firebird://user:password@hostname:3050/path/to/database.fdbโ€‹# MongoDB (SQL queries are auto-translated)TINA4_DATABASE_URL=mongodb://user:password@hostname:27017/database_name

Gotcha: Special characters in your database password -- @, #, :, / -- will break URL parsing. URL-encode them (@ becomes %40) or split the credentials into separate variables.

DB Query Cache#

VariableDefaultDescription
TINA4_DB_CACHEfalseEnable in-memory caching of query results.
TINA4_DB_CACHE_TTL30Cache time-to-live in seconds.

ORM#

VariableDefaultDescription
TINA4_ORM_PLURAL_TABLE_NAMESfalseAppend "s" to auto-generated table names. When false (default), Product maps to product. When true, Product maps to products.

CORS (Cross-Origin Resource Sharing)#

VariableDefaultDescription
TINA4_CORS_ORIGINS*Comma-separated allowed origins. * allows all. In production, list your actual domains.
TINA4_CORS_METHODSGET,POST,PUT,PATCH,DELETE,OPTIONSComma-separated HTTP methods allowed in cross-origin requests.
TINA4_CORS_HEADERSContent-Type,Authorization,X-Request-IDComma-separated headers the client is allowed to send.
TINA4_CORS_CREDENTIALStrueWhether the browser sends cookies and auth headers in cross-origin requests.
TINA4_CORS_MAX_AGE86400How long (seconds) the browser caches preflight responses. 86400 = 24 hours.

Gotcha: TINA4_CORS_ORIGINS=* combined with TINA4_CORS_CREDENTIALS=true is invalid per the CORS spec. Tina4 handles this automatically -- when origin is *, the credentials header is not sent.

Security Headers#

VariableDefaultDescription
TINA4_FRAME_OPTIONSSAMEORIGINX-Frame-Options header. Prevents clickjacking. Options: DENY, SAMEORIGIN.
TINA4_HSTS(empty/off)Strict-Transport-Security max-age in seconds. Set to 31536000 (1 year) in production with HTTPS.
TINA4_CSPdefault-src 'self'Content-Security-Policy header. Controls which resources the browser is allowed to load.
TINA4_REFERRER_POLICYstrict-origin-when-cross-originReferrer-Policy header. Controls what referrer info is sent with requests.
TINA4_PERMISSIONS_POLICYcamera=(), microphone=(), geolocation=()Permissions-Policy header. Disables browser features your app doesn't need.

The X-Content-Type-Options: nosniff and X-XSS-Protection: 0 headers are always set (no env variable -- these are security best practices).

Rate Limiter#

VariableDefaultDescription
TINA4_RATE_LIMIT100Maximum requests per window per IP address.
TINA4_RATE_WINDOW60Window duration in seconds. Default: 100 requests per 60 seconds.

The rate limiter adds three headers to every response:

X-RateLimit-Limit: 100X-RateLimit-Remaining: 85X-RateLimit-Reset: 60

When the limit is exceeded, the server returns 429 Too Many Requests.

Sessions#

VariableDefaultDescription
TINA4_SESSION_BACKENDfileSession storage backend. Options: file, redis, valkey, mongo, database.
TINA4_SESSION_PATHdata/sessionsDirectory for file-based sessions. Relative to the project root.
TINA4_SESSION_TTL3600Session expiry in seconds. Default: 1 hour.
TINA4_SESSION_SAMESITELaxSameSite cookie attribute. Options: Strict, Lax, None.

Redis/Valkey Session Backend#

VariableDefaultDescription
TINA4_SESSION_REDIS_HOST127.0.0.1Redis host.
TINA4_SESSION_REDIS_PORT6379Redis port.
TINA4_SESSION_REDIS_PASSWORD(none)Redis password.
TINA4_SESSION_REDIS_DB0Redis database number.
TINA4_SESSION_REDIS_PREFIXtina4:session:Key prefix for session data.
TINA4_SESSION_VALKEY_HOSTlocalhostValkey host.
TINA4_SESSION_VALKEY_PORT6379Valkey port.
TINA4_SESSION_VALKEY_PASSWORD(none)Valkey password.
TINA4_SESSION_VALKEY_DB0Valkey database number.
TINA4_SESSION_VALKEY_PREFIXtina4:session:Key prefix for session data.

MongoDB Session Backend#

VariableDefaultDescription
TINA4_SESSION_MONGO_URI(none)Full MongoDB connection URI. Overrides host/port.
TINA4_SESSION_MONGO_HOSTlocalhostMongoDB host.
TINA4_SESSION_MONGO_PORT27017MongoDB port.
TINA4_SESSION_MONGO_USERNAME(none)MongoDB username.
TINA4_SESSION_MONGO_PASSWORD(none)MongoDB password.
TINA4_SESSION_MONGO_DBtina4_sessionsMongoDB database name.
TINA4_SESSION_MONGO_COLLECTIONsessionsMongoDB collection for session data.

Queue#

VariableDefaultDescription
TINA4_QUEUE_BACKENDfileQueue storage backend. Options: file, rabbitmq, kafka, mongodb.
TINA4_QUEUE_PATHdata/queueDirectory for file-based queue storage (when using file backend).
TINA4_QUEUE_URL(none)Generic connection URL for queue backend.

RabbitMQ Queue Backend#

VariableDefaultDescription
TINA4_RABBITMQ_HOSTlocalhostRabbitMQ host.
TINA4_RABBITMQ_PORT5672RabbitMQ port.
TINA4_RABBITMQ_USERNAMEguestRabbitMQ username.
TINA4_RABBITMQ_PASSWORDguestRabbitMQ password.
TINA4_RABBITMQ_VHOST/RabbitMQ virtual host.

Kafka Queue Backend#

VariableDefaultDescription
TINA4_KAFKA_BROKERSlocalhost:9092Comma-separated Kafka broker addresses.
TINA4_KAFKA_GROUP_IDtina4_consumer_groupKafka consumer group ID.

MongoDB Queue Backend#

VariableDefaultDescription
TINA4_MONGO_URI(none)Full MongoDB connection URI. Overrides host/port.
TINA4_MONGO_HOSTlocalhostMongoDB host.
TINA4_MONGO_PORT27017MongoDB port.
TINA4_MONGO_USERNAME(none)MongoDB username.
TINA4_MONGO_PASSWORD(none)MongoDB password.
TINA4_MONGO_DBtina4MongoDB database name.
TINA4_MONGO_COLLECTIONtina4_queueMongoDB collection for queue messages.

Response Cache#

VariableDefaultDescription
TINA4_CACHE_BACKENDmemoryCache storage. Options: memory (in-process), file, redis, valkey, memcached, mongodb, database. Falls back to file if the configured backend is unreachable.
TINA4_CACHE_TTL60Default cache TTL in seconds for cached routes.
TINA4_CACHE_MAX_ENTRIES1000Maximum cached responses (memory backend). Oldest entries evicted at the limit.
TINA4_CACHE_DIRdata/cacheDirectory for file-based cache.
TINA4_CACHE_URLredis://localhost:6379Connection URL for remote cache backends (Redis, Valkey, Memcached, MongoDB). For database, falls back to TINA4_DATABASE_URL.
TINA4_CACHE_USERNAME(none)Username for the cache backend. May also be embedded in TINA4_CACHE_URL.
TINA4_CACHE_PASSWORD(none)Password for the cache backend. May also be embedded in TINA4_CACHE_URL (e.g. redis://:pass@host). Memcached is unauthenticated.

Logging#

VariableDefaultDescription
TINA4_LOG_LEVELERRORMinimum log level. Options: ALL, DEBUG, INFO, WARNING, ERROR.
TINA4_LOG_MAX_SIZE10Maximum log file size in MB before rotation.
TINA4_LOG_KEEP5Number of rotated log files to keep.

Messenger (Email / SMTP)#

Tina4 supports two naming conventions for SMTP variables. The SMTP_* variables are the primary names. The TINA4_MAIL_* variables are aliases that take precedence when both are set.

VariableAliasDefaultDescription
TINA4_MAIL_HOSTTINA4_MAIL_HOSTlocalhostSMTP server hostname.
TINA4_MAIL_PORTTINA4_MAIL_PORT587SMTP port. 587 (TLS), 465 (SSL), 25 (unencrypted).
TINA4_MAIL_USERNAMETINA4_MAIL_USERNAME(none)SMTP authentication username.
TINA4_MAIL_PASSWORDTINA4_MAIL_PASSWORD(none)SMTP authentication password.
TINA4_MAIL_FROMTINA4_MAIL_FROMnoreply@localhostDefault sender address.
TINA4_MAIL_FROM_NAMETINA4_MAIL_FROM_NAME(none)Sender display name.
TINA4_MAIL_ENCRYPTION-tlsConnection encryption. tls, ssl, or none.

IMAP (for reading email)#

VariableDefaultDescription
TINA4_MAIL_IMAP_HOST(falls back to TINA4MAILHOST)IMAP server hostname.
TINA4_MAIL_IMAP_PORT993IMAP port (993 = SSL).

Localization (i18n)#

VariableDefaultDescription
TINA4_LOCALEenDefault locale. Determines which translation file (src/locales/{locale}.json) is loaded.
TINA4_LOCALE_DIRsrc/localesDirectory containing translation JSON files.

Swagger / OpenAPI#

VariableDefaultDescription
TINA4_SWAGGER_TITLETina4 APIAPI title shown in Swagger UI.
TINA4_SWAGGER_VERSION1.0.0API version shown in Swagger UI.
TINA4_SWAGGER_DESCRIPTION(none)API description.

File Uploads#

VariableDefaultDescription
TINA4_MAX_UPLOAD_SIZE10485760Maximum upload size in bytes. Default: 10 MB.

WebSocket#

VariableDefaultDescription
TINA4_WS_PORT8080WebSocket server port (when running as separate process).
TINA4_WS_BACKPLANE(none)WebSocket backplane type. Set to redis to relay broadcasts across instances.
TINA4_WS_BACKPLANE_URLredis://localhost:6379Connection URL for the WebSocket backplane.

Services (Background Workers)#

VariableDefaultDescription
TINA4_SERVICE_DIRsrc/servicesDirectory for service worker scripts.
TINA4_SERVICE_SLEEP5Seconds between service worker ticks.

Dev Mailbox#

VariableDefaultDescription
TINA4_MAILBOX_DIRdata/mailboxDirectory for captured dev emails.

Minimal .env for Development#

Getting started? One line:

bash
TINA4_DEBUG=true

Everything else uses sensible defaults:

  • Binds to 0.0.0.0 on the framework's default port
  • SQLite database at data/app.db
  • File-based sessions (1 hour TTL)
  • File-based queue
  • In-memory response cache
  • CORS allows all origins
  • 100 requests per minute rate limit
  • CSRF protection enabled
  • Security headers active

One line. A working development environment. Add variables when you need them. Not before.

Minimal .env for Production#

bash
TINA4_DEBUG=falseTINA4_SECRET=a-very-long-random-string-at-least-32-charactersTINA4_DATABASE_URL=postgresql://app_user:strong_password@db-host:5432/myappTINA4_CORS_ORIGINS=https://myapp.comTINA4_HSTS=31536000TINA4_MAIL_HOST=smtp.sendgrid.netTINA4_MAIL_PORT=587TINA4_MAIL_USERNAME=apikeyTINA4_MAIL_PASSWORD=SG.xxxxxTINA4_MAIL_FROM=noreply@myapp.com

Ten lines. A production application. Debug disabled. Real database. Signed tokens. Locked CORS. HSTS enabled. Email configured. Everything else keeps its defaults.

Docker .env#

When running in Docker, HOST must be 0.0.0.0 so the container accepts connections from outside. This is already the default, but if you override it, keep this in mind:

bash
# Required for Docker -- do NOT set to 127.0.0.1HOST=0.0.0.0PORT=7145TINA4_DEBUG=false

Full .env Template#

Copy this to your .env.example as a starting point:

bash
# =============================================================================# Tina4 Environment Configuration# Copy this file to .env and fill in your values# =============================================================================โ€‹# --- Server ---HOST=0.0.0.0# PORT=7145TINA4_DEBUG=falseโ€‹# --- Authentication ---TINA4_SECRET=CHANGE_ME# TINA4_API_KEY=TINA4_TOKEN_LIMIT=60โ€‹# --- CSRF ---TINA4_CSRF=trueโ€‹# --- Database ---TINA4_DATABASE_URL=sqlite:///data/app.db# TINA4_DATABASE_USERNAME=# TINA4_DATABASE_PASSWORD=# TINA4_AUTOCOMMIT=falseโ€‹# --- DB Query Cache ---# TINA4_DB_CACHE=false# TINA4_DB_CACHE_TTL=30โ€‹# --- ORM ---# TINA4_ORM_PLURAL_TABLE_NAMES=falseโ€‹# --- CORS ---TINA4_CORS_ORIGINS=*TINA4_CORS_METHODS=GET,POST,PUT,PATCH,DELETE,OPTIONSTINA4_CORS_HEADERS=Content-Type,Authorization,X-Request-IDTINA4_CORS_CREDENTIALS=trueTINA4_CORS_MAX_AGE=86400โ€‹# --- Security Headers ---# TINA4_FRAME_OPTIONS=SAMEORIGIN# TINA4_HSTS=# TINA4_CSP=default-src 'self'# TINA4_REFERRER_POLICY=strict-origin-when-cross-origin# TINA4_PERMISSIONS_POLICY=camera=(), microphone=(), geolocation=()โ€‹# --- Rate Limiting ---TINA4_RATE_LIMIT=100TINA4_RATE_WINDOW=60โ€‹# --- Logging ---TINA4_LOG_LEVEL=ERROR# TINA4_LOG_MAX_SIZE=10# TINA4_LOG_KEEP=5โ€‹# --- Sessions ---TINA4_SESSION_BACKEND=file# TINA4_SESSION_PATH=data/sessionsTINA4_SESSION_TTL=3600# TINA4_SESSION_REDIS_HOST=127.0.0.1# TINA4_SESSION_REDIS_PORT=6379# TINA4_SESSION_VALKEY_HOST=localhost# TINA4_SESSION_VALKEY_PORT=6379โ€‹# --- Queue ---TINA4_QUEUE_BACKEND=file# TINA4_RABBITMQ_HOST=localhost# TINA4_RABBITMQ_PORT=5672# TINA4_RABBITMQ_USERNAME=guest# TINA4_RABBITMQ_PASSWORD=guest# TINA4_KAFKA_BROKERS=localhost:9092# TINA4_KAFKA_GROUP_ID=tina4_consumer_group# TINA4_MONGO_HOST=localhost# TINA4_MONGO_PORT=27017# TINA4_MONGO_DB=tina4# TINA4_MONGO_COLLECTION=tina4_queueโ€‹# --- Response Cache ---TINA4_CACHE_BACKEND=memoryTINA4_CACHE_TTL=60TINA4_CACHE_MAX_ENTRIES=1000โ€‹# --- Email (SMTP) ---# TINA4_MAIL_HOST=smtp.example.com# TINA4_MAIL_PORT=587# TINA4_MAIL_USERNAME=# TINA4_MAIL_PASSWORD=# TINA4_MAIL_FROM=noreply@example.comโ€‹# --- Localization ---TINA4_LOCALE=enโ€‹# --- Swagger ---TINA4_SWAGGER_TITLE=Tina4 APITINA4_SWAGGER_VERSION=1.0.0# TINA4_SWAGGER_DESCRIPTION=โ€‹# --- File Uploads ---# TINA4_MAX_UPLOAD_SIZE=10485760โ€‹# --- Services ---# TINA4_SERVICE_DIR=src/services# TINA4_SERVICE_SLEEP=5

Summary#

CountCategory
3Server (HOST, PORT, TINA4_DEBUG)
3Authentication (TINA4SECRET, TINA4APIKEY, TINA4TOKEN_LIMIT)
1CSRF (TINA4_CSRF)
4Database (TINA4DATABASEURL, USERNAME, PASSWORD, AUTOCOMMIT)
2DB query cache
5CORS
5Security headers
2Rate limiter
3Logging
14Sessions (base + Redis + Valkey + MongoDB)
15Queue (base + RabbitMQ + Kafka + MongoDB)
5Response cache
8Messenger (SMTP + IMAP)
2Localization
3Swagger
1File uploads
3WebSocket
2Services
1Dev mailbox
82Total

Every variable follows the same priority chain: constructor > .env > default. Every boolean is interpreted consistently across all four frameworks. Every variable has a sensible default that works for development without any configuration.

One file. Eighty-two knobs. Turn what you need. Leave the rest alone.