# Defaults — Default Deployment Files & Override System

## Overview

This module ships default deployment files inside the installed package and provides a resolution system that lets adapters override any file. The resolution system ensures adapters start with working defaults and only customize what they need to.

## Concepts

### Package Defaults

The package ships deployment files that provide a complete, working deployment for any adapter:

| File | Purpose |
|------|---------|
| `Dockerfile` | uv-based Python 3.12 slim build, exposes port 8096, runs uvicorn |
| `nginx.conf` | Production nginx routing Core traffic (nextapp + backend), 50M upload limit, SSE support |
| `docker-compose.dev.yml` | Development environment with Core services, live reload |
| `docker-compose.prod.yml` | Production with variable substitution, restart policies, health checks |
| `fluent-bit.conf` | CloudWatch Logs output via fluentd forward input, tenant-specific log stream |
| `prefix.lua` | Fluent Bit Lua script for container name prefixing in log lines |

These files live inside the installed package at `src/entr_adapter_core/defaults/` and are located at runtime via `importlib.resources`.

### Override Mechanisms

There are two override mechanisms, matched to the nature of each file type:

**File-system shadow (full replace):**
Used for files that are customized as a whole — `Dockerfile`, `nginx.conf`, `fluent-bit.conf`. If an adapter places a file with the same name in its `deploy/` directory, it completely replaces the package default. There is no merge — the override is the entire file.

**Compose-native merge:**
Used for Docker Compose files. The package defaults always serve as the base. If the adapter has a `docker-compose.override.yml` at its project root, it is stacked on top using Docker Compose's native `-f base -f override` mechanism. This allows selective overrides (adding a service, changing an environment variable) without replacing the entire compose file.

### Override Directory

The default override directory is `deploy/` relative to the adapter project root. This path is part of the public API — changing it constitutes a breaking change requiring a major version bump.

## Patterns

### Resolution Flow

When deployment files are needed (at build time or by the CLI), the resolution system:

1. Checks if an override exists in the adapter's `deploy/` directory
2. If found, uses the override (shadow) or appends it to the compose chain (merge)
3. If not found, uses the package default
4. Logs which source was used for every file — no silent fallbacks

### Map-Style Environment Variables

Docker Compose files use map-style environment variables throughout:

```yaml
# CORRECT — map-style, merges individual keys
environment:
  TENANT_NAME: my-tenant
  LOG_LEVEL: INFO

# WRONG — list-style, replaces entire block on override
environment:
  - TENANT_NAME=my-tenant
  - LOG_LEVEL=INFO
```

Map-style is required for proper compose override behavior. When an adapter's override file sets one environment variable, only that variable is overridden — the rest inherit from the base. List-style would replace the entire environment block.

### Resolution Logging

At startup, `log_resolution_summary()` produces a table at INFO level showing every deployment file, its resolved source (package default or adapter override), and the resolution mechanism used. This ensures transparency — operators always know exactly which files are in effect.

## Guidance

### Override Decision Table

| What you want to customize | How to do it |
|---------------------------|-------------|
| Environment variables | Compose override file (`docker-compose.override.yml` at adapter root) with map-style env vars |
| Dockerfile base image or build steps | Place custom `Dockerfile` in `deploy/` |
| Nginx config entirely | Place custom `nginx.conf` in `deploy/` |
| Fluent-bit config | Place custom `fluent-bit.conf` in `deploy/` |
| Compose services or volumes | Compose override file |
| Upload limits, timeouts | Already set generously in package defaults — override nginx only if needed |

### When to Override

Override only when the package default doesn't meet your needs. Common reasons:

- **Dockerfile**: Custom system packages, different Python version, multi-stage build changes
- **nginx.conf**: Custom routing rules, different upload limits, additional locations
- **fluent-bit.conf**: Different log destination (not CloudWatch), custom parsers
- **Compose override**: Additional services, custom volumes, environment variable overrides

### When NOT to Override

- Don't override just to change environment variables — use compose override instead of replacing the full compose file
- Don't copy a package default to `deploy/` "just in case" — you'll miss future improvements to the default
- Don't override `docker-compose.dev.yml` / `docker-compose.prod.yml` directly — use `docker-compose.override.yml` at the adapter root for compose-native merge

### Staying Current with Package Defaults

When the package updates a default file (documented in `CHANGELOG.md` under `Deployment File Changes`):

- **No override**: You get the update automatically — nothing to do
- **Shadow override**: Review the changelog entry to decide if your override needs updating. The package logs a warning if your override might be outdated.
- **Compose override**: Base changes merge with your override automatically. Review only if the changelog calls out a specific interaction.

## Override Reference

### Full Override Decision Table

| What you want to customize | How to do it | Override mechanism |
|---------------------------|-------------|-------------------|
| Environment variables | `docker-compose.override.yml` at adapter root with map-style env vars | Compose-native merge |
| Dockerfile base image or build steps | Place custom `Dockerfile` in `deploy/` | File-system shadow (full replace) |
| Nginx config entirely | Place custom `nginx.conf` in `deploy/` | File-system shadow (full replace) |
| Fluent-bit config | Place custom `fluent-bit.conf` in `deploy/` | File-system shadow (full replace) |
| Compose services or volumes | `docker-compose.override.yml` at adapter root | Compose-native merge |
| Upload limits, timeouts | Already set generously — override nginx only if needed | File-system shadow (if overriding) |

### Per-File Default Behavior

**`Dockerfile`** — uv-based Python 3.12 slim build (`ghcr.io/astral-sh/uv:python3.12-bookworm-slim`). Installs from `pyproject.toml` via `uv sync`. Exposes port 8096. Entry point: `uvicorn src.main:app --host 0.0.0.0 --port 8096`.

**`nginx.conf`** — Production nginx config routing Core traffic to `nextapp:3000` and `backend:8000`. `client_max_body_size 50M`. SSE support for `/api/files/status-stream`: `proxy_buffering off`, `proxy_cache off`, `proxy_read_timeout 86400s`. Gzip enabled. Standard proxy headers. Static asset caching. ELB health check handling. Note: the adapter is on the internal network only — not exposed via nginx.

**`fluent-bit.conf`** — Receives container logs via fluentd forward input on port 24224. CloudWatch Logs output to `/entr/production` log group with `${TENANT_SLUG}-production` stream name. Uses `prefix.lua` for container name prefixing.

**`docker-compose.dev.yml`** — Core services (nextapp, backend, core-nginx) plus adapter with live reload (`./src:/app/src` mount, `--reload` flag). Requires `DEFAULTS_DIR` env var (set by `entr-cli dev start`).

**`docker-compose.prod.yml`** — Self-contained production compose. Variable substitution: `${DOCKER_USER}`, `${TENANT_SLUG}`, `${ADAPTER_TAG}`, `${CORE_TAG}`. Restart policies (`always`). Health checks on nextapp, backend, and adapter. Fluentd logging driver. Resource limits.

### Per-File Override Checklists

**Dockerfile override checklist:**
- Port must remain **8096** (nginx upstream expects this)
- Entry point must target `src.main:app` (or your custom entry point)
- Must install `entr-adapter-core` dependency (via `uv sync` or `pip install`)
- Verify the working directory matches where your code is copied

**nginx.conf override checklist:**
- Production nginx routes Core traffic (nextapp + backend) — adapter is on internal network only
- Include SSE support settings (`proxy_buffering off`, `proxy_cache off`) for status stream endpoint
- Include `client_max_body_size` setting appropriate for your document sizes (default: 50M)
- Preserve standard proxy headers for proper request forwarding

**fluent-bit.conf override checklist:**
- Verify input reads Docker container logs (or your preferred log source)
- Verify output matches your log destination (CloudWatch, Elasticsearch, etc.)
- Include the tenant identifier in log group/stream naming for multi-tenant isolation

**Compose override — safe to override:**
- Adding environment variables (map-style only)
- Adding volumes or volume mounts
- Adding new services
- Overriding resource limits (memory, CPU)
- Overriding restart policies
- Adding `env_file` references

### Startup Resolution Log

The `log_resolution_summary()` function produces a table at startup showing every file and its source:

```
Deployment file resolution:
  Dockerfile              package_default   shadow
  nginx.conf              adapter_override  shadow
  fluent-bit.conf         package_default   shadow
  prefix.lua              package_default   shadow
  docker-compose.dev.yml  package_default   compose_merge
  docker-compose.prod.yml package_default   compose_merge
```

There are no silent fallbacks — every file's resolution is explicitly logged. If you see `adapter_override` for a file, it means your `deploy/` directory has an override in effect.

## Cross-References

- **API reference**: See docstrings in `resolve.py` for `resolve_file()`, `resolve_compose_files()`, `resolve_all_files()`, and `log_resolution_summary()` parameters and return types
- **Deployment file changes**: See `CHANGELOG.md` `Deployment File Changes` sections for history of changes to default files
- **Override directory constant**: `DEFAULT_OVERRIDE_DIR = "deploy/"` defined in `resolve.py`
- **CLI integration**: The ENTR CLI uses the resolution system to locate deployment files for `entr-cli dev start` and production builds
