# Configuration reference

Every Scryon configuration knob is an environment variable. They all have safe defaults so a vanilla `./mvnw spring-boot:run` works on a developer laptop. Production deployments override what they need.

The source of truth is `src/main/resources/application.yml` in `scryon-backend`.

## Core

| Variable           | Default                                   | Notes                                             |
| ------------------ | ----------------------------------------- | ------------------------------------------------- |
| `SERVER_PORT`      | `8080`                                    | HTTP listener port.                               |
| `DB_URL`           | `jdbc:postgresql://localhost:5432/scryon` | JDBC URL.                                         |
| `DB_USERNAME`      | `scryon`                                  | DB user.                                          |
| `DB_PASSWORD`      | `scryon`                                  | DB password.                                      |
| `FLYWAY_ENABLED`   | `true`                                    | Set to `false` only for the H2 test profile.      |
| `MAX_FILE_SIZE`    | `50MB`                                    | Hard cap per uploaded recording.                  |
| `MAX_REQUEST_SIZE` | `55MB`                                    | `MAX_FILE_SIZE` + room for the metadata envelope. |

## Security

| Variable                      | Default   | Notes                                                                      |
| ----------------------------- | --------- | -------------------------------------------------------------------------- |
| `SCRYON_API_KEY`              | `(empty)` | Optional shared-secret header. If unset the guard is off — local dev only. |
| `FIREBASE_PROJECT_ID`         | `(empty)` | When set, every `/api/**` request needs a valid Firebase ID token.         |
| `FIREBASE_CLIENT_EMAIL`       | `(empty)` | Service-account client email — enables Admin SDK verification.             |
| `FIREBASE_PRIVATE_KEY`        | `(empty)` | PEM-encoded service-account private key.                                   |
| `SCRYON_CORS_ALLOWED_ORIGINS` | `(empty)` | Comma-separated allowlist. Empty = no CORS headers.                        |

## Observability

| Variable                                    | Default                          | Notes                                        |
| ------------------------------------------- | -------------------------------- | -------------------------------------------- |
| `SCRYON_OBSERVABILITY_ENABLED`              | `true`                           | Master switch.                               |
| `SCRYON_REQUEST_LOGGING_ENABLED`            | `true`                           | Structured HTTP access log.                  |
| `SCRYON_PIPELINE_LOGGING_ENABLED`           | `true`                           | Per-stage `event=PIPELINE` log lines.        |
| `SCRYON_DEBUG_ENDPOINTS_ENABLED`            | `false`                          | Owner-scoped `/api/debug/calls/{id}/events`. |
| `SCRYON_TRACING_ENABLED`                    | `false`                          | Micrometer Observation spans.                |
| `MANAGEMENT_TRACING_ENABLED`                | `false`                          | Spring Boot tracing facade.                  |
| `OTEL_EXPORTER_OTLP_ENDPOINT`               | `(empty)`                        | OTLP collector URL.                          |
| `OTEL_TRACES_SAMPLER_ARG`                   | `0.1`                            | Trace sampling probability.                  |
| `SENTRY_DSN`                                | `(empty)`                        | Sentry project DSN. Empty = no events sent.  |
| `SENTRY_ENVIRONMENT`                        | `local`                          | Tags Sentry events.                          |
| `SENTRY_TRACES_SAMPLE_RATE`                 | `0.1`                            | Sentry performance sampling.                 |
| `MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE` | `health,info,metrics,prometheus` | Actuator exposure list.                      |

## Privacy

| Variable             | Default | Notes                                    |
| -------------------- | ------- | ---------------------------------------- |
| `REDACT_TRANSCRIPTS` | `true`  | Redact transcript text from `INFO` logs. |

> The hard rule "Scryon never persists raw audio" is enforced in code, not config. There is no env var to disable it.

## Transcription (Lemonfox)

| Variable                                        | Default                      | Notes                                          |
| ----------------------------------------------- | ---------------------------- | ---------------------------------------------- |
| `LEMONFOX_API_KEY`                              | `(empty)`                    | **Required.**                                  |
| `LEMONFOX_BASE_URL`                             | `https://api.lemonfox.ai/v1` | Override for self-hosted.                      |
| `LEMONFOX_MODEL`                                | `whisper-1`                  | Whisper variant.                               |
| `LEMONFOX_LANGUAGE`                             | `(auto)`                     | ISO-639-1 hint. Empty = autodetect.            |
| `LEMONFOX_TIMEOUT_SECONDS`                      | `180`                        | Per-request timeout.                           |
| `SCRYON_TRANSCRIPTION_CALLBACK_ENABLED`         | `false`                      | Async webhook mode.                            |
| `SCRYON_PUBLIC_BASE_URL`                        | `(empty)`                    | Required when callback mode is on.             |
| `SCRYON_WEBHOOK_SECRET`                         | `(empty)`                    | ≥ 32-byte HMAC secret for webhook URL signing. |
| `SCRYON_TRANSCRIPTION_CALLBACK_TIMEOUT_MINUTES` | `30`                         | Sweep stuck callbacks after this.              |

## Diarization (pyannoteAI)

| Variable                                   | Default                   | Notes                                                              |
| ------------------------------------------ | ------------------------- | ------------------------------------------------------------------ |
| `PYANNOTE_ENABLED`                         | `false`                   | Master switch. Fallback to Lemonfox built-in diarization when off. |
| `PYANNOTE_API_KEY`                         | `(empty)`                 | Required when enabled.                                             |
| `PYANNOTE_BASE_URL`                        | `https://api.pyannote.ai` |                                                                    |
| `PYANNOTE_MODEL`                           | `precision-2`             |                                                                    |
| `SCRYON_DIARIZATION_TIMEOUT_SECONDS`       | `300`                     | Polling deadline.                                                  |
| `SCRYON_DIARIZATION_POLL_INTERVAL_SECONDS` | `5`                       | Job poll cadence.                                                  |
| `SCRYON_DIARIZATION_MAX_RETRIES`           | `2`                       | Network + 5xx retries.                                             |
| `SCRYON_DIARIZATION_MAX_AUDIO_MINUTES`     | `30`                      | Skip pyannote above this (cost/latency guard).                     |
| `SCRYON_DIARIZATION_HINT_TWO_SPEAKERS`     | `true`                    | Send `numSpeakers: 2` for phone calls.                             |

## Voice embedding (opt-in)

| Variable                                  | Default    | Notes                             |
| ----------------------------------------- | ---------- | --------------------------------- |
| `SCRYON_VOICE_EMBEDDING_ENABLED`          | `false`    | Master switch. APIs 404 when off. |
| `SCRYON_VOICE_EMBEDDING_PROVIDER`         | `pyannote` | Reuses pyannote credentials.      |
| `SCRYON_VOICE_EMBEDDING_HIGH_THRESHOLD`   | `0.85`     | Score ≥ ⇒ HIGH match.             |
| `SCRYON_VOICE_EMBEDDING_MEDIUM_THRESHOLD` | `0.75`     | Score ≥ ⇒ MEDIUM match.           |
| `SCRYON_VOICE_SAMPLE_MIN_SECONDS`         | `15`       | Reject samples below.             |
| `SCRYON_VOICE_SAMPLE_MAX_SECONDS`         | `45`       | Reject samples above.             |
| `SCRYON_VOICE_SAMPLE_MAX_SIZE_MB`         | `10`       | Hard file-size cap.               |
| `SCRYON_VOICE_CONSENT_VERSION`            | `v1`       | Bump to require re-consent.       |

## Audio preprocessing

| Variable                                     | Default  | Notes                                    |
| -------------------------------------------- | -------- | ---------------------------------------- |
| `SCRYON_AUDIO_PREPROCESSING_ENABLED`         | `true`   | Mono + 16 kHz + loudnorm pipeline.       |
| `SCRYON_FFMPEG_PATH`                         | `ffmpeg` | Override for non-PATH installs.          |
| `SCRYON_AUDIO_PREPROCESSING_OUTPUT_FORMAT`   | `wav`    | Output container.                        |
| `SCRYON_AUDIO_PREPROCESSING_TIMEOUT_SECONDS` | `60`     | Per-file ffmpeg timeout.                 |
| `SCRYON_AUDIO_DENOISE_ENABLED`               | `true`   | High-pass + FFT denoise before loudnorm. |
| `SCRYON_AUDIO_HIGHPASS_HZ`                   | `80`     | HVAC rumble cutoff.                      |
| `SCRYON_AUDIO_DENOISE_NR_DB`                 | `12`     | Noise reduction strength (dB).           |
| `SCRYON_AUDIO_DENOISE_NOISE_FLOOR_DB`        | `-25`    | Estimated noise floor.                   |

## LLM (analysis)

| Variable              | Default                     | Notes         |
| --------------------- | --------------------------- | ------------- |
| `LLM_PROVIDER`        | `openai`                    |               |
| `LLM_API_KEY`         | `(empty)`                   | **Required.** |
| `LLM_BASE_URL`        | `https://api.openai.com/v1` |               |
| `LLM_MODEL`           | `gpt-4o-mini`               |               |
| `LLM_TIMEOUT_SECONDS` | `120`                       |               |
| `LLM_TEMPERATURE`     | `0.2`                       |               |

## Object storage

| Variable                              | Default         | Notes                                      |
| ------------------------------------- | --------------- | ------------------------------------------ |
| `OBJECT_STORAGE_PROVIDER`             | `local`         | `local` or `s3`.                           |
| `OBJECT_STORAGE_BUCKET`               | `(empty)`       | S3 bucket name.                            |
| `OBJECT_STORAGE_ENDPOINT`             | `(empty)`       | S3-compatible endpoint URL.                |
| `OBJECT_STORAGE_REGION`               | `auto`          |                                            |
| `OBJECT_STORAGE_ACCESS_KEY`           | `(empty)`       |                                            |
| `OBJECT_STORAGE_SECRET_KEY`           | `(empty)`       |                                            |
| `OBJECT_STORAGE_PATH_STYLE_ACCESS`    | `true`          | Disable for path-aware AWS S3.             |
| `OBJECT_STORAGE_LOCAL_PATH`           | `./var/storage` | Used only when provider = `local`.         |
| `OBJECT_STORAGE_TEMP_AUDIO_TTL_HOURS` | `24`            | How long temp audio survives before sweep. |

## Async / background jobs

| Variable                           | Default  | Notes                        |
| ---------------------------------- | -------- | ---------------------------- |
| `SCRYON_STALE_JOB_TIMEOUT_MINUTES` | `15`     | Mark jobs FAILED after this. |
| `SCRYON_SWEEP_INTERVAL_MS`         | `300000` | Sweeper period.              |
| `SCRYON_SWEEP_INITIAL_DELAY_MS`    | `60000`  | Startup delay.               |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.scryon.app/getting-started/configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
