From d19d226ad305b74f28937c68fec7f47f575b9995 Mon Sep 17 00:00:00 2001 From: Joachim Rosskopf Date: Fri, 22 May 2026 13:40:53 +0200 Subject: [PATCH] docs: self-packaging across architecture / decisions / security / CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of #40. Closes #50. Captures the rationale, mechanism, and operational surface of self-packaging (#41-#49) in the locations operators and future maintainers actually look. - docs/spec/ARCHITECTURE.md * New "Self-Packaging (optional)" subsection under Key Components, naming archive_io / selfpath / bundle_locator / macho_bundle / EmbeddedArchiveFileProvider / EmbeddedFileSystem / pack with file paths. * New "Self-Packaging Bootstrap" data-flow block that traces the sequence from main() through detectAndRegisterEmbeddedBundle to FileProviderFactory dispatch to RegisterEmbeddedFileSystem. - docs/spec/DESIGN_DECISIONS.md * New §9 "Self-Packaging via Appended ZIP" with four sub-decisions: 9a. ZIP appended after the executable -- why-not tar / 7z / custom container; the `bytes_in_last_block(1)` spike gotcha. 9b. Reuse IFileProvider -- why-not extract-to-tmpdir. 9c. embed:// DuckDB FileSystem -- why-not force everything through IFileProvider (streaming); the Glob / SeekPosition spike-runtime catch. 9d. macOS reserved-segment + re-codesign -- why-not append-and- ad-hoc-sign (notarisation). * Each item lists Decision / Rationale / Why-not alternatives / Tradeoffs, matching the file's existing structure. * Summary updated to add "Deployability" as the sixth design goal that §9 serves. - docs/spec/components/security.md * New "Secrets and the bundle" section covering the two enforcement mechanisms (pack-time deny list and runtime env-var contract). Lists every credential env var with its scope. * Cross-reference to DESIGN_DECISIONS §9 and CONFIG_REFERENCE §1.4. * Best-practices list grows an 8th item ("never bundle secrets") with a forward pointer to the section. * Source-files table grows `src/pack.cpp (IsSecretExcluded)`. - docs/CLI_REFERENCE.md * New "## 3. Self-Packaging Subcommands" section with full reference for `pack`, `info`, `unpack`, plus a macOS subsection covering the reserved-segment + codesign flow and the --macos-append legacy escape hatch. * Sections 4-7 renumbered (Environment Variables / Usage Examples / Signal Handling / Exit Codes); TOC updated to match. * Environment-variables table grows FLAPI_CONFIG, FLAPI_LOG_LEVEL, SOURCE_DATE_EPOCH, CODESIGN_IDENTITY entries. - AGENTS.md (target of the CLAUDE.md symlink) * New "6. Self-Packaging" entry under Core Concepts -- short operator-style overview, command examples, mechanism, secrets invariant, reproducibility note. Forward links to DESIGN_DECISIONS §9 and CLI_REFERENCE §3 for depth. --- AGENTS.md | 50 +++++++++++ docs/CLI_REFERENCE.md | 139 +++++++++++++++++++++++++++++-- docs/spec/ARCHITECTURE.md | 47 +++++++++++ docs/spec/DESIGN_DECISIONS.md | 126 ++++++++++++++++++++++++++++ docs/spec/components/security.md | 48 +++++++++++ 5 files changed, 402 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e17b684a..6dcce561 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -401,6 +401,56 @@ Template variables available: - `context.conn.*`: Connection properties (paths, credentials) - `context.auth.*`: Authentication context +### 6. Self-Packaging (single-binary deploy) + +The same `flapi` binary that serves the API can also fold an entire +config tree into itself, producing a self-contained executable +deployable via `scp`. + +```bash +# Pack a config tree into a new bundled binary +flapi pack --in ./examples --out flapi-prod + +# Inspect what's bundled +./flapi-prod info + +# Extract the bundle for debugging +./flapi-prod unpack --to /tmp/extracted + +# Run it -- serves the bundled config from any cwd +cd /tmp && ./flapi-prod +``` + +**How it works:** + +- A ZIP archive is appended after the executable (or, on macOS, + written into a reserved `__FLAPI/__bundle` Mach-O segment that + was allocated at link time -- 16 MiB default, knob + `FLAPI_RESERVED_BUNDLE_MIB`). Mach-O is re-codesigned so the + output is notarisable. +- At startup, `bundle_locator` either reverse-scans the EOCD + signature from EOF (Linux / Windows) or probes the reserved + section (macOS). On hit, entries are decompressed once into a + shared `ArchiveEntries`. +- `EmbeddedArchiveFileProvider` (implements `IFileProvider`) serves + config / SQL templates from that map. `FileProviderFactory` + dispatches non-remote paths to it when a bundle is present. +- For SQL templates that use `read_csv()` / `read_parquet()`, an + `EmbeddedFileSystem` is registered with DuckDB on the `embed://` + scheme, so `read_csv('embed://data/cities.csv')` resolves to the + same in-memory bytes. + +**Secrets never go in the bundle.** `pack` refuses files matching +`*.env`, `secrets/*`, `*.pem`, `*.key` by default. Credentials come +from environment variables at runtime (`AWS_*`, `GOOGLE_*`, `AZURE_*`, +`FLAPI_CONFIG_SERVICE_TOKEN`, `{{env.VARNAME}}` interpolation in +YAML). See [DESIGN_DECISIONS §9](docs/spec/DESIGN_DECISIONS.md#9-self-packaging-via-appended-zip) +for the rationale and [CLI_REFERENCE §3](docs/CLI_REFERENCE.md#3-self-packaging-subcommands) +for full subcommand options. + +**Reproducibility.** Set `SOURCE_DATE_EPOCH` (epoch seconds) before +`flapi pack` and the output is bit-identical across runs. + ## Key Patterns ### Safe Query Building Pattern diff --git a/docs/CLI_REFERENCE.md b/docs/CLI_REFERENCE.md index 9f81961f..c2b0d1a6 100644 --- a/docs/CLI_REFERENCE.md +++ b/docs/CLI_REFERENCE.md @@ -19,14 +19,19 @@ This document provides a complete reference for the `flapi` server executable's - [Validate Configuration](#validate-configuration---validate-config) - [Configuration Service](#configuration-service---config-service) - [Configuration Service Token](#configuration-service-token---config-service-token) -3. [Environment Variables](#3-environment-variables) -4. [Usage Examples](#4-usage-examples) +3. [Self-Packaging Subcommands](#3-self-packaging-subcommands) + - [`flapi pack`](#flapi-pack----create-a-self-contained-binary) + - [`flapi info`](#flapi-info----inspect-the-running-binarys-bundle) + - [`flapi unpack`](#flapi-unpack---to-dir----dump-the-bundle-for-debugging) + - [macOS notarisation specifics](#macos-notarisation-specifics) +4. [Environment Variables](#4-environment-variables) +5. [Usage Examples](#5-usage-examples) - [Basic Startup](#basic-startup) - [Development Mode](#development-mode) - [Production Mode](#production-mode) - [CI/CD Validation](#cicd-validation) -5. [Signal Handling](#5-signal-handling) -6. [Exit Codes](#6-exit-codes) +6. [Signal Handling](#6-signal-handling) +7. [Exit Codes](#7-exit-codes) - [Related Documentation](#related-documentation) --- @@ -396,12 +401,130 @@ export FLAPI_NO_TELEMETRY=1 --- -## 3. Environment Variables +## 3. Self-Packaging Subcommands + +flapi can fold its config tree (flapi.yaml + endpoint YAMLs + SQL +templates + small data files) into the binary itself, producing a +single self-contained artifact deployable via `scp`. The same binary +that serves the API also produces new bundled artifacts -- there is +no separate packager. + +See [DESIGN_DECISIONS.md §9](./spec/DESIGN_DECISIONS.md#9-self-packaging-via-appended-zip) +for the architectural rationale. + +### `flapi pack` -- create a self-contained binary + +``` +flapi pack --in --out [--allow-secrets] [--macos-append] +``` + +| Option | Required | Description | +|--------|----------|-------------| +| `--in` | yes | Directory containing `flapi.yaml` and friends. Walked recursively. | +| `--out` | yes | Path for the bundled output binary. Overwritten if it exists. | +| `--allow-secrets` | no | Bypass the default secret deny list. Testing only -- production users must never set this. | +| `--macos-append` | no | macOS only: append the archive after `__LINKEDIT` instead of overwriting the reserved `__FLAPI/__bundle` segment. **Not notarisable.** | + +**Default secret deny list** (refusal with non-zero exit, unless +`--allow-secrets`): + +- `*.env` at any depth +- `secrets/` segment at any depth +- `*.pem` at any depth +- `*.key` at any depth + +**Reproducible builds.** Set `SOURCE_DATE_EPOCH` to stamp every +archive entry with a deterministic mtime; the produced binary is +then bit-identical across runs given the same input. + +**Re-pack idempotence.** If the host binary already has a trailing +bundle, `pack` strips it from the _copy_ (not the running binary) +before appending the new one, so repeated invocations don't grow the +output. + +**Example:** + +```bash +SOURCE_DATE_EPOCH=1700000000 flapi pack --in ./examples --out flapi-prod +chmod +x flapi-prod # exec bits already preserved +scp flapi-prod user@host:/opt/flapi/ # one-file deploy +ssh user@host '/opt/flapi/flapi-prod' # serves bundled config from any cwd +``` + +> **Implementation:** `src/pack.cpp`, `src/archive_io.cpp` | **Tests:** `test/cpp/pack_test.cpp`, `test/integration/test_self_packaging.py` + +### `flapi info` -- inspect the running binary's bundle + +``` +flapi info +``` + +Prints the EOCD offset, bundle size, and entry list (with byte +counts). Exits non-zero with `"Bundle: none (filesystem mode)"` if +the binary has no appended (or in-section, on macOS) bundle. + +**Example:** + +```bash +$ ./flapi-prod info +Binary: /opt/flapi/flapi-prod +Bundle offset: 70123456 +Bundle size: 12534 bytes +Entries (17): + flapi.yaml (1024 bytes) + sqls/customers.yaml (412 bytes) + ... +``` + +### `flapi unpack --to ` -- dump the bundle for debugging + +``` +flapi unpack --to +``` + +Writes every bundle entry to `` (creating intermediate +directories as needed), preserving paths. Useful for diffing a +deployed bundle against a development tree. + +**Example:** + +```bash +$ ./flapi-prod unpack --to /tmp/extracted +Unpacked 17 entries to /tmp/extracted + +$ diff -ru ./examples /tmp/extracted +# (empty -- bundle matches source tree) +``` + +### macOS notarisation specifics + +On Darwin, `flapi` is linked with a reserved `__FLAPI/__bundle` +Mach-O section (default 16 MiB, knob `FLAPI_RESERVED_BUNDLE_MIB` at +CMake configure time). `flapi pack` overwrites this section in +place and re-invokes `codesign --force --sign $CODESIGN_IDENTITY` +(defaulting to `-` for ad-hoc) so the freshly bundled binary has a +fresh valid signature. The output is suitable for `notarytool +submit`. + +The `--macos-append` flag falls back to the Linux/Windows-style +trailing-bytes layout. Use it only for local debugging -- the +signature is intentionally invalid and the binary will fail +notarisation. + +> **Implementation:** `src/macho_bundle.cpp` | **Tests:** `test/cpp/macho_bundle_test.cpp`, `test/integration/test_self_packaging_macos.py` + +--- + +## 4. Environment Variables | Variable | Description | Used By | |----------|-------------|---------| +| `FLAPI_CONFIG` | Path to `flapi.yaml` (fallback for `-c`) | `--config` fallback | +| `FLAPI_LOG_LEVEL` | Log verbosity (fallback for `--log-level`); invalid values exit 1 | `--log-level` fallback | | `FLAPI_CONFIG_SERVICE_TOKEN` | Authentication token for configuration service API | `--config-service-token` fallback | | `FLAPI_NO_TELEMETRY` | Disable telemetry when set to `1`, `true`, or `yes` | `--no-telemetry` fallback | +| `SOURCE_DATE_EPOCH` | Mtime stamped on every entry by `flapi pack` (reproducible builds) | `flapi pack` | +| `CODESIGN_IDENTITY` | macOS only: identity passed to `codesign --sign` after `flapi pack`. Defaults to `-` (ad-hoc). | `flapi pack` | **Configuration File Variables:** @@ -418,7 +541,7 @@ See [Configuration Reference - Environment Variables](./CONFIG_REFERENCE.md#10-e --- -## 4. Usage Examples +## 5. Usage Examples ### Basic Startup @@ -474,7 +597,7 @@ fi --- -## 5. Signal Handling +## 6. Signal Handling | Signal | Behavior | |--------|----------| @@ -499,7 +622,7 @@ On receiving a shutdown signal, the server: --- -## 6. Exit Codes +## 7. Exit Codes | Code | Description | |------|-------------| diff --git a/docs/spec/ARCHITECTURE.md b/docs/spec/ARCHITECTURE.md index 2bdde66d..cd5e4b98 100644 --- a/docs/spec/ARCHITECTURE.md +++ b/docs/spec/ARCHITECTURE.md @@ -139,6 +139,23 @@ Storage and external data access: | **AuthMiddleware** | `src/auth_middleware.cpp` | JWT/Basic/OIDC authentication | | **RateLimitMiddleware** | `src/rate_limit_middleware.cpp` | Request rate limiting | +### Self-Packaging (optional) + +These components are loaded only when the running binary contains an +appended (or, on macOS, in-section) ZIP bundle. They let the same +artifact serve the API _and_ produce new bundled artifacts via +`flapi pack`. + +| Component | File | Purpose | +|-----------|------|---------| +| **archive_io** | `src/archive_io.cpp` | RAII wrapper around libarchive; reads/writes ZIPs in memory, with `bytes_in_last_block=1` and `SOURCE_DATE_EPOCH` mtime stamping for reproducible builds. | +| **selfpath** | `src/selfpath.cpp` | Cross-platform self-binary path (`/proc/self/exe`, `_NSGetExecutablePath`, `GetModuleFileNameW`). | +| **bundle_locator** | `src/bundle_locator.cpp` | Reverse-scans for the ZIP EOCD record (Linux/Windows); on macOS prefers the reserved `__FLAPI/__bundle` Mach-O section. Tolerates trailing zero padding. | +| **macho_bundle** | `src/macho_bundle.cpp` | 64-bit Mach-O header + LC_SEGMENT_64 parser; writes the archive into the reserved section in place and re-invokes `codesign`. | +| **EmbeddedArchiveFileProvider** | `src/embedded_archive_file_provider.cpp` | `IFileProvider` implementation backed by a `std::shared_ptr`. Sibling of `LocalFileProvider` / `DuckDBVFSProvider`. | +| **EmbeddedFileSystem** | `src/duckdb_embed_fs.cpp` | `duckdb::FileSystem` for the `embed://` scheme. Lets SQL templates do `read_csv('embed://data/x.csv')`. Same `ArchiveEntries` instance as `EmbeddedArchiveFileProvider`. | +| **pack** | `src/pack.cpp` | `flapi pack` / `info` / `unpack` subcommand logic. Enforces a default secret deny list (`*.env`, `secrets/*`, `*.pem`, `*.key`). | + ## Data Flow ### REST Request Flow @@ -167,6 +184,36 @@ Storage and external data access: For detailed request flows with sequence diagrams, see [REQUEST_LIFECYCLE.md](./REQUEST_LIFECYCLE.md). +### Self-Packaging Bootstrap + +When `flapi` starts, _before_ loading the config: + +``` +1. main() calls detectAndRegisterEmbeddedBundle() + ├─ bundle_locator::LocateBundleInSelf() + │ macOS: probe __FLAPI/__bundle Mach-O section first + │ fallback: reverse-scan EOCD signature from EOF + ├─ if bundle found: read slice → archive_io::ReadArchive() + └─ store entries in FileProviderFactory (process-wide shared_ptr) + +2. main() proceeds to initializeConfig() + ConfigLoader.loadYamlFile("flapi.yaml") + ├─ FileProviderFactory::CreateProvider("flapi.yaml") + │ bundle present + non-remote path → EmbeddedArchiveFileProvider + │ no bundle + non-remote → LocalFileProvider + │ any remote scheme → DuckDBVFSProvider + └─ provider.ReadFile() returns bytes (from bundle or disk) + +3. After DatabaseManager is up, main() calls + RegisterEmbeddedFileSystem() which adds the embed:// VFS to + DuckDB so SQL templates can `read_csv('embed://data/foo.csv')`. +``` + +If no bundle is present (Linux/Windows shipped without `pack`, or +the trailing 1 KiB has been truncated), all bundle-aware components +silently return nullopt and the binary serves from the local +filesystem -- existing behaviour, zero churn. + ## Protocol Support flAPI supports two protocols from a unified configuration: diff --git a/docs/spec/DESIGN_DECISIONS.md b/docs/spec/DESIGN_DECISIONS.md index 4418df24..5ff346c9 100644 --- a/docs/spec/DESIGN_DECISIONS.md +++ b/docs/spec/DESIGN_DECISIONS.md @@ -291,6 +291,131 @@ request: --- +## 9. Self-Packaging via Appended ZIP + +**Decision:** Bundle the config tree (`flapi.yaml` + endpoint YAMLs + +SQL templates + small data files) into the flapi executable itself, +producing a single self-contained artifact deployable via `scp`. +Four sub-decisions form the design: + +### 9a. ZIP appended after the executable + +**Why ZIP, not tar/7z/custom container:** + +- The ZIP End-of-Central-Directory (EOCD) record is _designed_ to be + found by a reverse scan from EOF -- that is precisely why ZIPs + appended to SFX headers, AppImage payloads, etc. remain valid. +- ELF and PE loaders ignore trailing bytes after their own structures; + the appended ZIP is invisible at execution time. (Mach-O is the + awkward case -- see 9d.) +- `libarchive` reads and writes ZIPs from memory buffers without + touching disk, and is already vcpkg-available. + +**Why-not the alternatives:** + +- **tar:** no EOCD-equivalent self-locator; we'd have to invent one. +- **7z:** modern C++ surface is nicer (`bit7z`), but the format + doesn't tolerate arbitrary leading bytes as gracefully and lacks + ZIP's reverse-scan property. +- **Custom container:** every byte of complexity is one we'd need to + defend against drift. ZIP is documented, ubiquitous, debuggable + with `unzip -l`. + +**The spike caught one ZIP-specific gotcha:** libarchive's default +output rounds up to a 10240-byte tar-block boundary, padding with +zeros. That pushes the EOCD record off file-EOF and a naive reverse +scan misses it. Fix in `archive_io.cpp`: +```cpp +archive_write_set_bytes_in_last_block(a, 1); +``` + +### 9b. Reuse the existing `IFileProvider` abstraction + +flapi already routes every file read through `IFileProvider` +(`src/include/vfs_adapter.hpp`), with two implementations: local disk +and DuckDB's VFS (for `s3://`, `gs://`, etc). Adding a third -- +`EmbeddedArchiveFileProvider` -- serves config files straight from +the decompressed in-memory archive. The `FileProviderFactory` picks +it whenever a bundle is detected at startup. + +**Why-not extract-to-tmpdir:** + +- Race conditions on parallel `pack` invocations on the same host. +- File-permission surprises (umask, immutable tmpfs). +- Disk pressure on small container images. +- No way for SQL templates to reference files inside an extracted + tree _by their bundle-relative path_, which we want for + reproducible config. + +The result: `ConfigLoader`, `SqlTemplateProcessor`, endpoint +handlers, and everything else read through `IFileProvider` exactly +as before. The factory's dispatch is the only seam. + +### 9c. `embed://` DuckDB FileSystem for SQL-side reads + +SQL templates often contain `read_csv('data/foo.csv')` or +`read_parquet('s3://...')`. These bypass `IFileProvider` entirely +and hit DuckDB's `VirtualFileSystem`. Registering a custom +`duckdb::FileSystem` for the `embed://` scheme makes +`read_csv('embed://data/foo.csv')` resolve to the same in-memory +ArchiveEntries map. + +**Why a separate filesystem (vs. forcing every read through +IFileProvider):** + +- DuckDB's CSV/Parquet readers stream data with seek-based access. + Going through `IFileProvider::ReadFile` would materialise the + whole file as a `std::string` first -- defeating streaming. +- The DuckDB `embed://` filesystem subclasses `duckdb::FileSystem` + and serves bytes from the shared `ArchiveEntries` pointer + directly, so DuckDB streams as if from a real file. + +**Spike-caught requirement:** `Glob()` and `SeekPosition()` _must_ +be overridden, not just `OpenFile` / `Read` / `Seek`. `read_csv()` +expands paths via `Glob` before opening; the base `FileSystem` +throws "not implemented" rather than no-oping. + +### 9d. Reserved-segment + re-codesign on macOS + +Mach-O is the awkward case: appending bytes after `__LINKEDIT` +invalidates the codesign signature, breaking notarisation. + +**Decision:** allocate a placeholder `__FLAPI/__bundle` section at +link time (16 MiB default, configurable via +`FLAPI_RESERVED_BUNDLE_MIB`). `flapi pack` overwrites the section +in place rather than appending, then invokes `codesign --force +--sign $CODESIGN_IDENTITY` (defaulting to `-` for ad-hoc) so the +signature covers the new bytes. + +**Why-not:** the trailing-append-and-resign approach used by the +spike works for ad-hoc use but produces a binary that fails +notarisation. The reserved-segment approach is the +industry-standard fix used by AppImage, PyInstaller, Wails. + +**Tradeoffs:** +- (+) Single artifact deploys via `scp`; container images shrink + (`COPY sqls/` step removed). +- (+) Reproducible builds (`SOURCE_DATE_EPOCH` + sorted entries) -- + CI asserts `sha256(pack(x)) == sha256(pack(x))`. +- (+) Existing operators see _zero_ change -- if no bundle is + appended, all code paths fall back to filesystem mode. +- (-) Bundled binaries are immutable; live edits require a rebuild. +- (-) Reserved-segment size is a hard cap at link time (default + 16 MiB; rebuild with a larger value if needed). +- (-) Universal/fat Mach-O binaries are out of scope (parser handles + thin 64-bit only); release artifacts are per-architecture thin. + +**Source files:** `src/archive_io.cpp`, `src/bundle_locator.cpp`, +`src/selfpath.cpp`, `src/embedded_archive_file_provider.cpp`, +`src/duckdb_embed_fs.cpp`, `src/macho_bundle.cpp`, `src/pack.cpp` + +**Tests:** 35+ Catch2 unit tests across the listed source files; +8 pytest integration tests (`test/integration/test_self_packaging.py`); +4 macOS-specific tests (skipped on non-Darwin); CI smoke jobs on all +four supported platforms (#49 / `.github/workflows/build.yaml`). + +--- + ## Summary These design decisions prioritize: @@ -299,5 +424,6 @@ These design decisions prioritize: 3. **Flexibility** - Support multiple protocols from unified config 4. **Maintainability** - Clear separation of concerns 5. **Performance** - Caching and efficient query execution +6. **Deployability** - One artifact ships everything (#9 self-packaging) For implementation details, see the [component documentation](./components/). diff --git a/docs/spec/components/security.md b/docs/spec/components/security.md index 94225849..dc542709 100644 --- a/docs/spec/components/security.md +++ b/docs/spec/components/security.md @@ -395,6 +395,9 @@ mcp: 5. **Rate limit** - Protect against abuse 6. **Whitelist env vars** - Only expose needed environment variables 7. **Use HTTPS** - Enable TLS in production +8. **Never bundle secrets** - Credentials come from environment + variables at runtime, never from a config file checked into version + control. See _Secrets and the bundle_ below. ```yaml https: @@ -403,6 +406,50 @@ https: ssl_key_file: /path/to/key.pem ``` +## Secrets and the bundle + +When `flapi pack` produces a self-contained binary, it must _not_ +include credentials. A bundled binary that contains a database +password is itself a secret: it can be `scp`-ed, shared, attached to +a bug report, or pushed to a public registry by mistake. The single +artifact value disappears the moment the artifact is sensitive. + +Two mechanisms enforce this: + +1. **Pack-time refusal of the default deny list.** `Pack()` walks + the input tree and aborts with a non-zero exit if any file + matches: + - `*.env` at any depth (e.g. `.env`, `config/.env`) + - `secrets/` segment at any depth (e.g. `secrets/db.token`, + `nested/secrets/api.key`) + - `*.pem` at any depth + - `*.key` at any depth + + The error message names the offending file and points at the + `--allow-secrets` testing-only override. The override exists so + we can integration-test the deny list; production users must not + flip it. + +2. **Runtime expects credentials from the environment.** Every + credential flapi understands is read from a documented + environment variable -- never from a bundled YAML field: + - AWS S3: `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_REGION` + - GCS: `GOOGLE_APPLICATION_CREDENTIALS` / `GOOGLE_CLOUD_PROJECT` + - Azure: `AZURE_STORAGE_CONNECTION_STRING` / `AZURE_STORAGE_ACCOUNT` / `AZURE_STORAGE_KEY` + - Mgmt API: `FLAPI_CONFIG_SERVICE_TOKEN` + - JWT: configured via `{{env.JWT_SECRET}}` against a + whitelisted env var + + YAML strings may interpolate `{{env.VARNAME}}` for any env var + declared in `environment-whitelist`. The whitelist is itself part + of the configuration, so it ships in the bundle -- which is fine + because it names env vars by _key_, not value. + +See [DESIGN_DECISIONS.md §9](../DESIGN_DECISIONS.md#9-self-packaging-via-appended-zip) +for the broader rationale and +[CONFIG_REFERENCE.md §1.4](../../CONFIG_REFERENCE.md) for the +12-factor checklist of every env var flapi reads. + ## Source Files | File | Purpose | @@ -414,6 +461,7 @@ https: | `src/oidc_jwks_manager.cpp` | JWKS key management | | `src/request_validator.cpp` | Input validation | | `src/rate_limit_middleware.cpp` | Rate limiting | +| `src/pack.cpp` (`IsSecretExcluded`) | Default secret deny list for `flapi pack` | ## Related Documentation