Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sdurutr436/stay-sidekick/llms.txt

Use this file to discover all available pages before exploring further.

Stay Sidekick is configured via three .env files — the root .env for PostgreSQL credentials and Docker Compose settings, backend/.env for the Flask API, and web/.env for the public static site. All three are versioned as .env.example files in the repository. The root and backend files must be copied and populated with real values; web/.env ships with a Cloudflare Turnstile test key that works in development without any edits.
SECRET_KEY, JWT_SECRET_KEY, and FERNET_KEY must all be set to unique, randomly generated values in production. The placeholder strings shipped in .env.example are for illustrative purposes only — using them in production leaves the application completely insecure.

Root .env — PostgreSQL and Docker Compose

This file is read by Docker Compose to configure the postgres service and inject DATABASE_URL into the backend service. Copy it with:
cp .env.example .env
VariableRequiredDefaultDescription
POSTGRES_DBYesstay_sidekickName of the PostgreSQL database
POSTGRES_USERYespostgresPostgreSQL username
POSTGRES_PASSWORDYespostgresPostgreSQL password — must be changed for production
DATABASE_URLYespostgresql://postgres:postgres@postgres:5432/stay_sidekickFull connection URI. Host postgres is the Docker Compose service name. Update to match any changes to POSTGRES_USER or POSTGRES_PASSWORD.
TURNSTILE_SITE_KEYNo1x00000000000000000000AA (Cloudflare test key)Cloudflare Turnstile site key, passed as a build argument to the web service. The default test key is always valid in development but should be replaced with a real site key from the Cloudflare dashboard before going to production.
.env files do not interpolate variables. DATABASE_URL must be a fully literal string — you cannot write postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@... inside the file itself.

backend/.env — Flask API

This file is loaded exclusively by the backend service. Copy it with:
cp backend/.env.example backend/.env

Flask core

VariableRequiredDefaultDescription
FLASK_ENVYesdevelopmentSet to production on Railway
SECRET_KEYYesplaceholderPrimary Flask secret key, used for session signing and CSRF. Must be ≥ 32 random characters.
PORTNo5000Port Gunicorn listens on. Railway injects this automatically; Docker Compose ignores it.

CORS

VariableRequiredDefaultDescription
ALLOWED_ORIGINSYeshttp://localhost:4200Comma-separated list of allowed origins (no spaces). Docker Compose overrides this to http://localhost automatically. On Railway, set this to the public HTTPS URL of your nginx service.

JWT

VariableRequiredDefaultDescription
JWT_SECRET_KEYYesplaceholderSecret key for signing HS256 JWT access tokens issued by the panel
JWT_ACCESS_TOKEN_HOURSNo1Access token lifetime in hours

Encryption

VariableRequiredDefaultDescription
FERNET_KEYYes(empty)Fernet symmetric key used to encrypt PMS and AI API keys stored in PostgreSQL (common/crypto.py). Must be generated fresh for every environment — see below.

Cloudflare Turnstile

VariableRequiredDefaultDescription
TURNSTILE_SECRET_KEYNoplaceholderServer-side secret key for verifying Turnstile challenge tokens. Obtained from the Cloudflare dashboard alongside the site key.
TURNSTILE_VERIFY_URLNohttps://challenges.cloudflare.com/turnstile/v0/siteverifyTurnstile verification endpoint. No need to change this unless Cloudflare updates the URL.

Google OAuth

VariableRequiredDefaultDescription
GOOGLE_CLIENT_IDNoplaceholderOAuth 2.0 client ID from Google Cloud Console. Required only if using the Google Contacts sync feature.
GOOGLE_CLIENT_SECRETNoplaceholderOAuth 2.0 client secret from Google Cloud Console
GOOGLE_REDIRECT_URINohttp://localhost/api/contactos/google/callbackCallback URI registered in Google Cloud Console. Use https://your-domain/api/contactos/google/callback on Railway.

Frontend redirect

VariableRequiredDefaultDescription
FRONTEND_BASE_URLNohttp://localhostBase URL of the Angular SPA, used to construct post-OAuth redirect URLs. Set to the public HTTPS nginx URL on Railway.

Mailgun

VariableRequiredDefaultDescription
MAIL_GUN_API_KEYNoplaceholderMailgun private API key. Found under Account → Security → API Keys.
MAIL_GUN_DOMAINNoplaceholderVerified Mailgun sending domain (e.g. mg.yourdomain.com)
MAIL_GUN_API_URLNohttps://api.eu.mailgun.netMailgun API base URL. Use https://api.mailgun.net for US region accounts.
MAIL_FROMNoplaceholderFrom: address on outgoing emails and recipient of public contact form submissions. Falls back to noreply@<MAIL_GUN_DOMAIN> if left empty.

Discord webhooks

VariableRequiredDefaultDescription
DISCORD_WEBHOOK_URLNoplaceholderWebhook for the company sign-up request channel (/solicitud form)
DISCORD_WEBHOOK_CONTACT_URLNoplaceholderWebhook for the general contact channel (/empresa/contacto form)
DISCORD_WEBHOOK_OPERATIONS_URLNoplaceholderWebhook for the operations/alerts channel

Rate limiting

VariableRequiredDefaultDescription
RATE_LIMIT_CONTACTNo5/hourRate limit applied per IP on the public contact and sign-up form endpoints. Uses Flask-Limiter notation (e.g. 10/minute, 100/day).

AI

VariableRequiredDefaultDescription
AI_DEFAULT_PROVIDERNogeminiDefault AI provider for the Communications Vault assistant. Accepted values: gemini, openai, claude.
AI_DEFAULT_MODELNogemini/gemini-2.0-flashLiteLLM model string for the default provider (e.g. gpt-4o-mini, claude-haiku-3)
AI_DEFAULT_API_KEYNoplaceholderAPI key for the default provider. Used for companies that have not configured their own BYOK key.
AI_FREE_LIMIT_DAILYNo100Maximum AI requests per company per day on the shared free tier
AI_FREE_LIMIT_WEEKLYNo500Maximum AI requests per company per week on the shared free tier
AI_PROMPT_ADMIN_IPSNo127.0.0.1Comma-separated list of IPs authorised to manage system prompts via the admin endpoint

Smoobu integration

VariableRequiredDefaultDescription
SMOOBU_API_KEYNoplaceholderSmoobu REST API key for the property management system integration. Generated in Smoobu under Settings → API.

Database

VariableRequiredDefaultDescription
DATABASE_URLYespostgresql://postgres:postgres@postgres:5432/stay_sidekickPostgreSQL connection URI. In Docker Compose this is injected automatically from the root .env. On Railway, set this as a variable reference — see below.
On Railway, always set DATABASE_URL as a variable reference rather than a hardcoded string:
DATABASE_URL=${{ Postgres.DATABASE_URL }}
Railway resolves this to the live connection string from the managed PostgreSQL plugin and updates it automatically if credentials are rotated.

web/.env — Public static site (11ty)

This file is loaded by the web service at container startup. It is versioned as web/.env.example in the repository. Unlike the other two .env files, it ships pre-populated with a Cloudflare Turnstile test site key that is always valid in development, so no edits are needed for local use. Copy it with:
cp web/.env.example web/.env
VariableRequiredDefaultDescription
TURNSTILE_SITE_KEYNo1x00000000000000000000AA (Cloudflare test key)Public Cloudflare Turnstile site key embedded in the contact form HTML. The default test key always passes in development. Replace with a real site key from the Cloudflare Turnstile dashboard before going to production.
TURNSTILE_SITE_KEY is also passed as a Docker build argument to the web service via docker-compose.yml. The value in web/.env is used at runtime; the build argument value (TURNSTILE_SITE_KEY from the root .env) is baked into the static HTML at build time. For local development both default to the same Cloudflare test key.

Generating required secrets

Three variables must be set to freshly generated cryptographic values in every environment. You can generate all three with the Python standard library and the cryptography package (already a backend dependency). SECRET_KEY and JWT_SECRET_KEY — random URL-safe token, 32+ bytes:
python -c "import secrets; print(secrets.token_urlsafe(32))"
FERNET_KEY — 32-byte base64-encoded key required by the cryptography library:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Run each command separately and paste the output as the variable value. Never reuse keys across environments, and never commit real keys to version control.

Build docs developers (and LLMs) love