Skip to main content

Environment Configuration

The WispHub API uses environment variables for configuration, managed through Pydantic Settings. This approach provides type safety, validation, and flexible configuration sources.

Configuration File

All configuration is centralized in app/core/config.py:
app/core/config.py
from pydantic import Field
from typing import Tuple
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", case_sensitive=True)

    # WispHub Net API authentication
    WISPHUB_NET_KEY: str
    WISPHUB_NET_HOST: str

    # Ticket business rules
    MAX_ACTIVE_TICKETS_PER_ZONE: int = 3
    ACTIVE_TICKET_STATES: Tuple[int, ...] = (1,)
    DEFAULT_TICKET_STATUS: int = 1
    MAX_TICKET_RESOLUTION_DAYS: int = 3

    @property
    def TICKETS_URL(self) -> str:
        return f"{self.WISPHUB_NET_HOST}/api/tickets/"
    
    @property
    def CLIENTS_URL(self) -> str:
        return f"{self.WISPHUB_NET_HOST}/api/clientes/"
    
    @property
    def PLANS_URL(self) -> str:
        return f"{self.WISPHUB_NET_HOST}/api/plan-internet/"
    
    @property
    def PLANS_PPPOE_URL(self):
        return f"{self.WISPHUB_NET_HOST}/api/plan-internet/pppoe/"
    
    @property
    def PLANS_QUEUE_URL(self):
        return f"{self.WISPHUB_NET_HOST}/api/plan-internet/queue/"
    
    @property
    def TASKS_URL(self) -> str:
        return f"{self.WISPHUB_NET_HOST}/api/tasks/"

settings = Settings()

Environment Variables

Required Variables

These variables must be set for the API to function:
Type: strPurpose: API key for authenticating with WispHub NetExample:
WISPHUB_NET_KEY=abc123def456ghi789jkl
Security: Never commit this to version control. Keep it in .env which should be in .gitignore.Obtaining: Contact your WispHub administrator to generate an API key with appropriate permissions.
Type: str (URL)Purpose: Base URL of your WispHub Net instanceExample:
WISPHUB_NET_HOST=https://api.wisphub.net
Format: Must include protocol (https:// or http://) and should not have trailing slash.Common values:
  • https://api.wisphub.net (official WispHub)
  • https://yourcompany.wisphub.net (custom domain)
  • http://localhost:8080 (local WispHub instance for testing)

Required Variables — JWT Authentication

These variables are required for the JWT authentication system:
Type: strPurpose: Secret key used to sign and verify JWT tokens. Must be a cryptographically random string.Example:
JWT_SECRET_KEY=a3f9b2c1d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2
Generate a secure key:
openssl rand -hex 32
Security: This is the most sensitive value. Exposure allows forging JWT tokens and bypassing authentication.
Type: strPurpose: Algorithm used to sign JWT tokens.Default: HS256Example:
JWT_ALGORITHM=HS256
Supported: HS256 (HMAC-SHA256). This is the standard for symmetric JWT signing.
Type: intPurpose: How long JWT tokens remain valid, in minutes.Default: 60 (1 hour)Example:
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=120
Tuning:
  • Shorter expiry = better security, more frequent token refreshes
  • Longer expiry = better UX for long-running processes (e.g. bots)
Type: strPurpose: Username for the single API user. Used in POST /api/v1/auth/token.Example:
API_USERNAME=admin
Type: str (bcrypt hash)Purpose: Bcrypt hash of the API user’s password. The plain-text password is never stored.Example:
API_PASSWORD_HASH=$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGnJn5Tu
Generate a hash:
from passlib.context import CryptContext
ctx = CryptContext(schemes=["bcrypt"], deprecated="auto")
print(ctx.hash("your_password"))
Security: Never commit or log this value. Even though it’s a hash, it should be treated as a secret.

Optional Variables (Business Rules)

These variables have sensible defaults but can be customized:
Type: intPurpose: Maximum number of active support tickets allowed per zone to prevent technician saturationDefault: 3Example:
MAX_ACTIVE_TICKETS_PER_ZONE=5
Impact: When this limit is reached, new ticket creation requests for that zone will be rejected with a ZONE_LIMIT_REACHED response.Tuning: Adjust based on:
  • Number of technicians per zone
  • Average ticket resolution time
  • Acceptable customer wait times
Type: Tuple[int, ...]Purpose: Ticket status IDs considered “active” for zone limit calculationsDefault: (1,) (status ID 1, typically “Open”)Example:
ACTIVE_TICKET_STATES=(1,2,3)
Format: Comma-separated integers in parenthesesCommon WispHub Status IDs:
  • 1: Open
  • 2: In Progress
  • 3: Pending
  • 4: Resolved
  • 5: Closed
Check your WispHub instance’s ticket status configuration to match the IDs correctly.
Type: intPurpose: Default status ID assigned to newly created ticketsDefault: 1 (typically “Open”)Example:
DEFAULT_TICKET_STATUS=2
Usage: When creating tickets via POST /api/v1/tickets, this status is automatically assigned.
Type: intPurpose: Expected number of business days for ticket resolutionDefault: 3Example:
MAX_TICKET_RESOLUTION_DAYS=5
Usage: Used to calculate estimated resolution dates when creating tickets. Excludes weekends in the calculation.

.env File Example

Create a .env file in the project root:
.env
# WispHub Net Authentication (REQUIRED)
WISPHUB_NET_KEY=your_api_key_here
WISPHUB_NET_HOST=https://api.wisphub.net

# JWT Authentication (REQUIRED)
JWT_SECRET_KEY=your_very_long_random_secret_key_here
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60
API_USERNAME=admin
API_PASSWORD_HASH=$2b$12$...  # bcrypt hash

# Business Rules (OPTIONAL - defaults shown)
MAX_ACTIVE_TICKETS_PER_ZONE=3
ACTIVE_TICKET_STATES=(1,)
DEFAULT_TICKET_STATUS=1
MAX_TICKET_RESOLUTION_DAYS=3
Add .env to your .gitignore file:
.gitignore
.env
.env.*
!.env.example

Environment-Specific Configuration

Development Environment

.env.development
WISPHUB_NET_KEY=dev_test_key_12345
WISPHUB_NET_HOST=http://localhost:8080
JWT_SECRET_KEY=dev_secret_not_for_production
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=1440
API_USERNAME=admin
API_PASSWORD_HASH=$2b$12$...
MAX_ACTIVE_TICKETS_PER_ZONE=10

Production Environment

.env.production
WISPHUB_NET_KEY=prod_secure_key_xyz789abc
WISPHUB_NET_HOST=https://api.wisphub.net
JWT_SECRET_KEY=prod_long_random_key_from_openssl
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60
API_USERNAME=admin
API_PASSWORD_HASH=$2b$12$...
MAX_ACTIVE_TICKETS_PER_ZONE=3
Load the appropriate file:
# Development
cp .env.development .env

# Production  
cp .env.production .env

Computed Properties

The Settings class provides computed URL properties based on WISPHUB_NET_HOST:
# These are automatically constructed
settings.TICKETS_URL      # {HOST}/api/tickets/
settings.CLIENTS_URL      # {HOST}/api/clientes/
settings.PLANS_URL        # {HOST}/api/plan-internet/
settings.PLANS_PPPOE_URL  # {HOST}/api/plan-internet/pppoe/
settings.PLANS_QUEUE_URL  # {HOST}/api/plan-internet/queue/
settings.TASKS_URL        # {HOST}/api/tasks/
You don’t need to set these manually - they’re derived from WISPHUB_NET_HOST.

Configuration Validation

Pydantic automatically validates configuration on startup:

Type Checking

# ✅ Valid
MAX_ACTIVE_TICKETS_PER_ZONE=5

# ❌ Invalid - will raise ValidationError
MAX_ACTIVE_TICKETS_PER_ZONE=five
Error:
pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
MAX_ACTIVE_TICKETS_PER_ZONE
  Input should be a valid integer

Required Field Checking

# ❌ Missing WISPHUB_NET_KEY
Error:
pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
WISPHUB_NET_KEY
  Field required

Using Settings in Code

Import and use the global settings instance:
from app.core.config import settings

# Access configuration
api_key = settings.WISPHUB_NET_KEY
max_tickets = settings.MAX_ACTIVE_TICKETS_PER_ZONE

# Use computed URLs
url = settings.CLIENTS_URL  # https://api.wisphub.net/api/clientes/

Docker Configuration

Using —env-file

docker run -d \
  --name wisphub_api_server \
  -p 8000:8000 \
  --env-file .env \
  wisphubapi:latest

Using -e flags

docker run -d \
  --name wisphub_api_server \
  -p 8000:8000 \
  -e WISPHUB_NET_KEY=abc123 \
  -e WISPHUB_NET_HOST=https://api.wisphub.net \
  -e MAX_ACTIVE_TICKETS_PER_ZONE=5 \
  wisphubapi:latest

Docker Compose

docker-compose.yml
version: '3.8'

services:
  wisphub-api:
    image: wisphubapi:latest
    env_file:
      - .env
    environment:
      MAX_ACTIVE_TICKETS_PER_ZONE: 5
    ports:
      - "8000:8000"
Environment variables defined in the environment section override those from env_file.

Kubernetes Configuration

For Kubernetes deployments, use ConfigMaps and Secrets:

Secret (for sensitive data)

wisphub-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: wisphub-api-secret
type: Opaque
stringData:
  WISPHUB_NET_KEY: "your_api_key_here"

ConfigMap (for non-sensitive data)

wisphub-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: wisphub-api-config
data:
  WISPHUB_NET_HOST: "https://api.wisphub.net"
  MAX_ACTIVE_TICKETS_PER_ZONE: "3"
  MAX_TICKET_RESOLUTION_DAYS: "3"

Deployment

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wisphub-api
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: api
        image: wisphubapi:latest
        envFrom:
        - configMapRef:
            name: wisphub-api-config
        - secretRef:
            name: wisphub-api-secret

Troubleshooting

Issue: “Field required” error

Cause: Missing required environment variable Solution: Ensure .env file exists and contains all required variables:
# Check file exists
ls -la .env

# Verify contents
cat .env | grep WISPHUB_NET_KEY

Issue: “Input should be a valid integer”

Cause: Type mismatch (e.g., string instead of integer) Solution: Remove quotes from numeric values:
# ❌ Wrong
MAX_ACTIVE_TICKETS_PER_ZONE="3"

# ✅ Correct
MAX_ACTIVE_TICKETS_PER_ZONE=3

Issue: Configuration not loading in Docker

Cause: .env file not mounted or environment not passed Solution: Verify environment is being passed:
# Check environment inside container
docker exec wisphub_api_server env | grep WISPHUB

Issue: Case sensitivity problems

Cause: Environment variable names must match exactly (case-sensitive) Solution: Use uppercase for all variables:
# ❌ Wrong
wisphub_net_key=abc123

# ✅ Correct  
WISPHUB_NET_KEY=abc123

Security Best Practices

1

Never Commit Secrets

Add .env to .gitignore before committing:
echo ".env" >> .gitignore
git add .gitignore
git commit -m "Add .env to gitignore"
2

Use .env.example

Create a template without sensitive values:
.env.example
WISPHUB_NET_KEY=your_api_key_here
WISPHUB_NET_HOST=https://api.wisphub.net
MAX_ACTIVE_TICKETS_PER_ZONE=3
Commit this to version control as a reference.
3

Restrict File Permissions

chmod 600 .env
This makes the file readable only by the owner.
4

Use Secrets Management in Production

For production:
  • Docker: Docker secrets
  • Kubernetes: Kubernetes secrets
  • Cloud: AWS Secrets Manager, Azure Key Vault, GCP Secret Manager