diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 789cc33c..69e32483 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1314,9 +1314,43 @@ jobs:
if: failure()
run: docker compose -f ci/docker-compose.bdd.yml logs --no-color
+ # Anti-drift gate (S30.03): regenerate the committed beta-stack integration
+ # manifest from CMake and fail if it differs from what is checked in. Keeps
+ # docs/generated/beta-stack-manifest.txt honest against the pack source lists
+ # (the generator reads each pack's INTERFACE_SOURCES). Uses the cpputest-freertos
+ # image because it exports the FreeRTOS / lwIP / Mbed TLS / FatFs upstream paths,
+ # so every SolidSyslog::* pack target the manifest references is defined.
+ verify-manifest:
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/davidcozens/cpputest-freertos:sha-0b93766
+ options: --user root
+ env:
+ GIT_CONFIG_COUNT: 1
+ GIT_CONFIG_KEY_0: safe.directory
+ GIT_CONFIG_VALUE_0: '*'
+
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
+
+ - name: Regenerate the beta-stack manifest
+ shell: bash
+ run: |
+ cmake -S . -B build/manifest -DBUILD_TESTING=OFF \
+ -DSOLIDSYSLOG_MANIFEST_PACKS="LwipRaw;LwipRawDnsResolver;MbedTls;FreeRtos;FatFs" \
+ -DSOLIDSYSLOG_MANIFEST_OUTPUT="$(pwd)/docs/generated/beta-stack-manifest.txt"
+
+ - name: Fail on drift
+ shell: bash
+ run: |
+ git diff --exit-code docs/generated/beta-stack-manifest.txt \
+ || { echo "::error::docs/generated/beta-stack-manifest.txt is stale — regenerate with the manifest target (see docs/getting-started.md)"; exit 1; }
+
summary:
if: always() && github.event_name == 'pull_request'
- needs: [build-linux-gcc, build-linux-tunable-override, build-linux-clang, sanitize-linux-gcc, coverage-linux-gcc, analyze-tidy, analyze-tidy-freertos-plustcp, analyze-tidy-freertos-lwip, analyze-cppcheck, analyze-format, analyze-iwyu, analyze-iwyu-freertos-plustcp, analyze-iwyu-freertos-lwip, bdd-linux-syslog-ng, build-windows-msvc, bdd-windows-otel, integration-linux-openssl, integration-linux-mbedtls, integration-windows-openssl, build-freertos-host-tdd-plustcp, build-freertos-target-plustcp, bdd-freertos-qemu-plustcp, build-freertos-target-lwip, bdd-freertos-qemu-lwip]
+ needs: [build-linux-gcc, build-linux-tunable-override, build-linux-clang, sanitize-linux-gcc, coverage-linux-gcc, analyze-tidy, analyze-tidy-freertos-plustcp, analyze-tidy-freertos-lwip, analyze-cppcheck, analyze-format, analyze-iwyu, analyze-iwyu-freertos-plustcp, analyze-iwyu-freertos-lwip, bdd-linux-syslog-ng, build-windows-msvc, bdd-windows-otel, integration-linux-openssl, integration-linux-mbedtls, integration-windows-openssl, build-freertos-host-tdd-plustcp, build-freertos-target-plustcp, bdd-freertos-qemu-plustcp, build-freertos-target-lwip, bdd-freertos-qemu-lwip, verify-manifest]
runs-on: ubuntu-latest
permissions:
contents: read
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 02d9992e..8341ef3b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,6 +5,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(LayerGuard)
solidsyslog_enforce_layering()
include(Iwyu)
+include(Manifest)
# Default to C11 (for the optional C11 atomics counter), but let an externally
# supplied standard win so the pre-release C99 portability check can build the
@@ -329,3 +330,10 @@ if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING)
enable_testing()
add_subdirectory(Tests)
endif()
+
+# Integration manifest (S30.03) — generated from the configured targets (Core
+# SOURCES + each selected pack's INTERFACE_SOURCES) so the non-CMake
+# file/include/-D list can't drift from the build. Runs last, after every pack
+# target is defined. `cmake --build
--target manifest` prints it. See
+# cmake/Manifest.cmake.
+solidsyslog_generate_manifest()
diff --git a/DEVLOG.md b/DEVLOG.md
index 1ec509f3..b410debb 100644
--- a/DEVLOG.md
+++ b/DEVLOG.md
@@ -1,5 +1,53 @@
# Dev Log
+## 2026-06-10 — S30.03 generate the integration manifest from CMake
+
+E30's anti-drift story. The non-CMake file/include/`-D` manifest is now generated
+from the build instead of hand-listed in the doc, so it can't drift from what the
+packs ship.
+
+### Decisions
+- **`cmake/Manifest.cmake` reads the targets.** The generator pulls the `.c` lists
+ from the Core lib's `SOURCES` and each selected pack's `INTERFACE_SOURCES`
+ (populated in S30.02) — the volatile data comes from the build, never
+ hand-maintained. Stable per-pack knowledge (which config header each pack needs)
+ stays as a small curated map.
+- **Selection via `SOLIDSYSLOG_MANIFEST_PACKS`** (`;`-list of pack short-names;
+ empty = every defined `SolidSyslog::`). Core is always included. The host
+ Pattern-A adapters baked into the Core target (POSIX/Windows/OpenSSL/C11 atomics)
+ are filtered OUT — they're host-build conveniences with no place in an embedded
+ manifest. Include dirs are de-duplicated.
+- **`manifest` target** prints the generated file; generation runs at configure
+ time (writes `${BINARY_DIR}/solidsyslog-manifest.txt`, and also to
+ `SOLIDSYSLOG_MANIFEST_OUTPUT` when set). Print uses a `-P` helper
+ (`cmake/PrintManifest.cmake`) so it works on the 3.16 floor (`cmake -E cat` is
+ 3.18+).
+- **Committed sample + gating CI diff** (David's call, accepting a new required
+ check despite the epic's "don't add gates" note). The beta-stack manifest is
+ committed at `docs/generated/beta-stack-manifest.txt`; the new `verify-manifest`
+ CI job regenerates it in the cpputest-freertos image and `git diff --exit-code`s.
+ getting-started.md now points at that generated file (its hand-listed `.c` blocks
+ are gone) + shows the regenerate command.
+- Beta sample now includes the DNS resolver (matches the FreeRtosLwip BDD target,
+ which uses DNS) — so the manifest demonstrates the opt-in component +
+ `-DLWIP_DNS=1`.
+
+### Verification (local, cpputest-freertos image)
+- `manifest` target emits a correct manifest; Core list has no Platform/ leak;
+ include dirs de-duplicated.
+- The committed beta sample matches a fresh regeneration (`git diff --exit-code`
+ clean — the gate passes and would catch real drift).
+- Default-selection (`cmake --preset debug`, all 7 packs) configures + full junit
+ build passes.
+
+### Follow-up for David
+- **Add `verify-manifest` to branch-protection required checks** (manual GitHub
+ step) so the drift gate actually blocks merges.
+
+### Deferred
+- JSON manifest variant (story said optional/"if cheap" — skipped for now, no
+ consumer yet).
+
## 2026-06-10 — S30.02 consumer-friendly CMake umbrellas + dogfood
E30's CI-risk story. Made the six Pattern-B platform packs consumable in one line
diff --git a/cmake/Manifest.cmake b/cmake/Manifest.cmake
new file mode 100644
index 00000000..0e3217dc
--- /dev/null
+++ b/cmake/Manifest.cmake
@@ -0,0 +1,181 @@
+# Integration manifest generator (S30.03).
+#
+# Emits the file / include-dir / -D manifest a NON-CMake integrator (IAR / Keil /
+# MPLAB / CCS / hand Makefile) needs to compile SolidSyslog into their own
+# project. The .c file lists are read straight from the build targets — the Core
+# library's SOURCES and each selected pack's INTERFACE_SOURCES (populated in
+# S30.02) — so the manifest can never drift from what the packs actually ship.
+#
+# Selection: SOLIDSYSLOG_MANIFEST_PACKS is a ;-list of pack short-names (without
+# the SolidSyslog:: prefix), e.g. "LwipRaw;LwipRawDnsResolver;MbedTls;FreeRtos;FatFs".
+# Empty (the default) means "every SolidSyslog:: target that this configure
+# defined". Core is always included.
+#
+# Output: written to ${CMAKE_BINARY_DIR}/solidsyslog-manifest.txt at configure
+# time, and printed by `cmake --build --target manifest`. If
+# SOLIDSYSLOG_MANIFEST_OUTPUT is set it is ALSO written there (used to refresh the
+# committed docs/generated sample, which CI diff-checks for drift).
+
+set(SOLIDSYSLOG_MANIFEST_PACKS "" CACHE STRING
+ "Packs to include in the generated integration manifest (;-list of short \
+names without the SolidSyslog:: prefix). Empty = all defined packs.")
+set(SOLIDSYSLOG_MANIFEST_OUTPUT "" CACHE FILEPATH
+ "Optional extra path to also write the generated manifest to (e.g. the \
+committed docs/generated sample).")
+
+# Every pack the generator knows about, in manifest display order. Each carries
+# the integrator-supplied config header(s) that pack requires (stable knowledge;
+# the volatile .c lists come from the targets, not from here).
+set(_SOLIDSYSLOG_MANIFEST_KNOWN_PACKS
+ FreeRtos PlusTcp LwipRaw LwipRawDnsResolver MbedTls FatFs PlusFat)
+set(_SOLIDSYSLOG_MANIFEST_CFG_FreeRtos "FreeRTOSConfig.h")
+set(_SOLIDSYSLOG_MANIFEST_CFG_PlusTcp "FreeRTOSConfig.h, FreeRTOSIPConfig.h")
+set(_SOLIDSYSLOG_MANIFEST_CFG_LwipRaw "lwipopts.h")
+set(_SOLIDSYSLOG_MANIFEST_CFG_LwipRawDnsResolver "lwipopts.h (with LWIP_DNS=1)")
+set(_SOLIDSYSLOG_MANIFEST_CFG_MbedTls "mbedtls_config.h")
+set(_SOLIDSYSLOG_MANIFEST_CFG_FatFs "ffconf.h")
+set(_SOLIDSYSLOG_MANIFEST_CFG_PlusFat "FreeRTOSFATConfig.h")
+
+# Make an absolute path repo-relative for display; pass others through unchanged.
+function(_solidsyslog_manifest_relpath OUT_VAR PATH)
+ set(_p "${PATH}")
+ if(IS_ABSOLUTE "${_p}")
+ file(RELATIVE_PATH _p "${CMAKE_SOURCE_DIR}" "${_p}")
+ endif()
+ set(${OUT_VAR} "${_p}" PARENT_SCOPE)
+endfunction()
+
+# Append the .c sources of an INTERFACE pack target (repo-relative) to OUT_VAR.
+function(_solidsyslog_manifest_pack_sources OUT_VAR TARGET)
+ set(_lines "")
+ get_target_property(_srcs "${TARGET}" INTERFACE_SOURCES)
+ if(_srcs)
+ foreach(_s ${_srcs})
+ _solidsyslog_manifest_relpath(_rel "${_s}")
+ string(APPEND _lines "${_rel}\n")
+ endforeach()
+ endif()
+ set(${OUT_VAR} "${_lines}" PARENT_SCOPE)
+endfunction()
+
+function(solidsyslog_generate_manifest)
+ # Resolve the selection: explicit list, else every known pack that exists.
+ set(_selected "")
+ if(SOLIDSYSLOG_MANIFEST_PACKS)
+ set(_selected ${SOLIDSYSLOG_MANIFEST_PACKS})
+ else()
+ foreach(_pack ${_SOLIDSYSLOG_MANIFEST_KNOWN_PACKS})
+ if(TARGET SolidSyslog::${_pack})
+ list(APPEND _selected ${_pack})
+ endif()
+ endforeach()
+ endif()
+
+ string(REPLACE ";" ", " _selected_display "${_selected}")
+ if(NOT _selected_display)
+ set(_selected_display "(none — Core only)")
+ endif()
+
+ # --- Header ---------------------------------------------------------------
+ set(_m "# SolidSyslog integration manifest — GENERATED by CMake (S30.03).\n")
+ string(APPEND _m "# Do not edit by hand. Regenerate with: cmake --build --target manifest\n")
+ string(APPEND _m "# Selected packs: ${_selected_display}\n")
+ string(APPEND _m "#\n")
+ string(APPEND _m "# A non-CMake integrator (IAR / Keil / MPLAB / CCS / Make) compiles the .c\n")
+ string(APPEND _m "# files below, with the include dirs on the compiler path, against their own\n")
+ string(APPEND _m "# config headers. See docs/getting-started.md for the walkthrough.\n\n")
+
+ # --- Source files ---------------------------------------------------------
+ string(APPEND _m "## Source files (.c) — compile all of these\n\n")
+ string(APPEND _m "# Core (always required):\n")
+ get_target_property(_core_srcs SolidSyslog SOURCES)
+ foreach(_s ${_core_srcs})
+ if(IS_ABSOLUTE "${_s}")
+ # The host Pattern-A adapters (Posix / Windows / OpenSSL / C11 atomics)
+ # are baked into the Core target by CMake auto-detect. They are
+ # host-build conveniences and have no place in a (cross / embedded)
+ # integration manifest — keep only the portable Core/Source set.
+ string(FIND "${_s}" "/Core/Source/" _in_core)
+ if(_in_core EQUAL -1)
+ continue()
+ endif()
+ _solidsyslog_manifest_relpath(_rel "${_s}")
+ else()
+ set(_rel "Core/Source/${_s}")
+ endif()
+ string(APPEND _m "${_rel}\n")
+ endforeach()
+
+ foreach(_pack ${_selected})
+ if(NOT TARGET SolidSyslog::${_pack})
+ message(WARNING
+ "Manifest: SolidSyslog::${_pack} is not a defined target in this "
+ "configuration; skipping. (Is its upstream env path set?)")
+ else()
+ string(APPEND _m "\n# ${_pack}:\n")
+ _solidsyslog_manifest_pack_sources(_pack_srcs SolidSyslog::${_pack})
+ string(APPEND _m "${_pack_srcs}")
+ endif()
+ endforeach()
+
+ # --- Include directories --------------------------------------------------
+ string(APPEND _m "\n## Include directories\n\n")
+ set(_incdirs "Core/Interface" "Core/Source")
+ foreach(_pack ${_selected})
+ if(TARGET SolidSyslog::${_pack})
+ get_target_property(_incs SolidSyslog::${_pack} INTERFACE_INCLUDE_DIRECTORIES)
+ if(_incs)
+ foreach(_i ${_incs})
+ _solidsyslog_manifest_relpath(_rel "${_i}")
+ list(APPEND _incdirs "${_rel}")
+ endforeach()
+ endif()
+ endif()
+ endforeach()
+ list(REMOVE_DUPLICATES _incdirs)
+ foreach(_d ${_incdirs})
+ string(APPEND _m "${_d}\n")
+ endforeach()
+ string(APPEND _m "# Plus, located in YOUR project: the upstream library include dirs (lwIP,\n")
+ string(APPEND _m "# Mbed TLS, FreeRTOS, FatFs) and the directory holding your config headers.\n")
+
+ # --- Required defines -----------------------------------------------------
+ string(APPEND _m "\n## Required defines\n\n")
+ string(APPEND _m "-DSOLIDSYSLOG_USER_TUNABLES_FILE=\"my_tunables.h\" # your tunable overrides (optional)\n")
+ list(FIND _selected "LwipRawDnsResolver" _has_dns)
+ if(NOT _has_dns EQUAL -1)
+ string(APPEND _m "-DLWIP_DNS=1 # required by SolidSyslog::LwipRawDnsResolver\n")
+ endif()
+ list(FIND _selected "MbedTls" _has_mbedtls)
+ if(NOT _has_mbedtls EQUAL -1)
+ string(APPEND _m "# Mbed TLS: point MBEDTLS_USER_CONFIG_FILE (or MBEDTLS_CONFIG_FILE) at your mbedtls config,\n")
+ string(APPEND _m "# and use the SAME config when building the mbedTLS library itself.\n")
+ endif()
+
+ # --- Config headers you supply --------------------------------------------
+ string(APPEND _m "\n## Integrator-supplied config headers\n\n")
+ foreach(_pack ${_selected})
+ set(_cfg "${_SOLIDSYSLOG_MANIFEST_CFG_${_pack}}")
+ if(_cfg)
+ string(APPEND _m "${_cfg} (${_pack})\n")
+ endif()
+ endforeach()
+
+ # --- Emit -----------------------------------------------------------------
+ set(_out "${CMAKE_BINARY_DIR}/solidsyslog-manifest.txt")
+ file(WRITE "${_out}" "${_m}")
+ set(SOLIDSYSLOG_MANIFEST_FILE "${_out}" CACHE INTERNAL "Generated manifest path")
+ if(SOLIDSYSLOG_MANIFEST_OUTPUT)
+ file(WRITE "${SOLIDSYSLOG_MANIFEST_OUTPUT}" "${_m}")
+ message(STATUS "Manifest also written to ${SOLIDSYSLOG_MANIFEST_OUTPUT}")
+ endif()
+
+ # `cmake --build --target manifest` prints the generated file.
+ if(NOT TARGET manifest)
+ add_custom_target(manifest
+ COMMAND ${CMAKE_COMMAND} -DSOLIDSYSLOG_MANIFEST_FILE=${_out}
+ -P ${CMAKE_SOURCE_DIR}/cmake/PrintManifest.cmake
+ VERBATIM
+ COMMENT "SolidSyslog integration manifest (${_selected_display})")
+ endif()
+endfunction()
diff --git a/cmake/PrintManifest.cmake b/cmake/PrintManifest.cmake
new file mode 100644
index 00000000..e4f8405f
--- /dev/null
+++ b/cmake/PrintManifest.cmake
@@ -0,0 +1,8 @@
+# Helper for the `manifest` custom target: print the generated manifest to
+# stdout. Run in script mode (cmake -P) so it works on the project's CMake 3.16
+# floor (cmake -E cat is 3.18+).
+if(NOT DEFINED SOLIDSYSLOG_MANIFEST_FILE OR NOT EXISTS "${SOLIDSYSLOG_MANIFEST_FILE}")
+ message(FATAL_ERROR "Manifest file not found: ${SOLIDSYSLOG_MANIFEST_FILE}")
+endif()
+file(READ "${SOLIDSYSLOG_MANIFEST_FILE}" _contents)
+message("${_contents}")
diff --git a/docs/generated/beta-stack-manifest.txt b/docs/generated/beta-stack-manifest.txt
new file mode 100644
index 00000000..3feca92f
--- /dev/null
+++ b/docs/generated/beta-stack-manifest.txt
@@ -0,0 +1,136 @@
+# SolidSyslog integration manifest — GENERATED by CMake (S30.03).
+# Do not edit by hand. Regenerate with: cmake --build --target manifest
+# Selected packs: LwipRaw, LwipRawDnsResolver, MbedTls, FreeRtos, FatFs
+#
+# A non-CMake integrator (IAR / Keil / MPLAB / CCS / Make) compiles the .c
+# files below, with the include dirs on the compiler path, against their own
+# config headers. See docs/getting-started.md for the walkthrough.
+
+## Source files (.c) — compile all of these
+
+# Core (always required):
+Core/Source/SolidSyslog.c
+Core/Source/SolidSyslogStatic.c
+Core/Source/SolidSyslogError.c
+Core/Source/SolidSyslogConfigLock.c
+Core/Source/SolidSyslogSwitchingSender.c
+Core/Source/SolidSyslogSwitchingSenderStatic.c
+Core/Source/SolidSyslogBuffer.c
+Core/Source/SolidSyslogFormatter.c
+Core/Source/SolidSyslogTimestampFormatter.c
+Core/Source/SolidSyslogMessageFormatter.c
+Core/Source/SolidSyslogMetaSd.c
+Core/Source/SolidSyslogMetaSdStatic.c
+Core/Source/SolidSyslogPassthroughBuffer.c
+Core/Source/SolidSyslogPassthroughBufferStatic.c
+Core/Source/SolidSyslogCircularBuffer.c
+Core/Source/SolidSyslogCircularBufferStatic.c
+Core/Source/SolidSyslogPoolAllocator.c
+Core/Source/SolidSyslogMutex.c
+Core/Source/SolidSyslogNullMutex.c
+Core/Source/SolidSyslogSender.c
+Core/Source/SolidSyslogSenderHealth.c
+Core/Source/SolidSyslogNullSender.c
+Core/Source/SolidSyslogStore.c
+Core/Source/SolidSyslogNullSd.c
+Core/Source/SolidSyslogNullStore.c
+Core/Source/SolidSyslogNullBuffer.c
+Core/Source/SolidSyslogNullBlockDevice.c
+Core/Source/SolidSyslogNullSecurityPolicy.c
+Core/Source/SolidSyslogCrc16.c
+Core/Source/SolidSyslogCrc16Policy.c
+Core/Source/SolidSyslogStream.c
+Core/Source/SolidSyslogNullStream.c
+Core/Source/SolidSyslogStructuredData.c
+Core/Source/SolidSyslogSdValue.c
+Core/Source/SolidSyslogSdElement.c
+Core/Source/SolidSyslogHeaderField.c
+Core/Source/SolidSyslogEndpointHost.c
+Core/Source/SolidSyslogTimeQualitySd.c
+Core/Source/SolidSyslogTimeQualitySdStatic.c
+Core/Source/SolidSyslogOriginSd.c
+Core/Source/SolidSyslogOriginSdStatic.c
+Core/Source/SolidSyslogFile.c
+Core/Source/SolidSyslogNullFile.c
+Core/Source/SolidSyslogBlockStore.c
+Core/Source/SolidSyslogBlockStoreStatic.c
+Core/Source/RecordStore.c
+Core/Source/RecordStoreStatic.c
+Core/Source/BlockSequence.c
+Core/Source/BlockSequenceStatic.c
+Core/Source/SolidSyslogBlockDevice.c
+Core/Source/SolidSyslogFileBlockDevice.c
+Core/Source/SolidSyslogFileBlockDeviceStatic.c
+Core/Source/SolidSyslogUdpPayload.c
+Core/Source/SolidSyslogAtomicCounter.c
+Core/Source/SolidSyslogNullAtomicCounter.c
+Core/Source/SolidSyslogResolver.c
+Core/Source/SolidSyslogNullResolver.c
+Core/Source/SolidSyslogDatagram.c
+Core/Source/SolidSyslogNullDatagram.c
+Core/Source/SolidSyslogUdpSender.c
+Core/Source/SolidSyslogUdpSenderStatic.c
+Core/Source/SolidSyslogStreamSender.c
+Core/Source/SolidSyslogStreamSenderStatic.c
+
+# LwipRaw:
+Platform/LwipRaw/Source/SolidSyslogLwipRawAddress.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawAddressStatic.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawDatagram.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawDatagramStatic.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawTcpStream.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawTcpStreamStatic.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawResolver.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawResolverStatic.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawMarshal.c
+
+# LwipRawDnsResolver:
+Platform/LwipRaw/Source/SolidSyslogLwipRawDnsResolver.c
+Platform/LwipRaw/Source/SolidSyslogLwipRawDnsResolverStatic.c
+
+# MbedTls:
+Platform/MbedTls/Source/SolidSyslogMbedTlsStream.c
+Platform/MbedTls/Source/SolidSyslogMbedTlsStreamStatic.c
+Platform/MbedTls/Source/SolidSyslogMbedTlsHmacSha256Policy.c
+Platform/MbedTls/Source/SolidSyslogMbedTlsHmacSha256PolicyStatic.c
+Platform/MbedTls/Source/SolidSyslogMbedTlsAesGcmPolicy.c
+Platform/MbedTls/Source/SolidSyslogMbedTlsAesGcmPolicyStatic.c
+
+# FreeRtos:
+Platform/FreeRtos/Source/SolidSyslogFreeRtosMutex.c
+Platform/FreeRtos/Source/SolidSyslogFreeRtosMutexStatic.c
+Platform/FreeRtos/Source/SolidSyslogFreeRtosSysUpTime.c
+
+# FatFs:
+Platform/FatFs/Source/SolidSyslogFatFsFile.c
+Platform/FatFs/Source/SolidSyslogFatFsFileStatic.c
+
+## Include directories
+
+Core/Interface
+Core/Source
+Platform/LwipRaw/Interface
+Platform/LwipRaw/Source
+Platform/MbedTls/Interface
+Platform/MbedTls/Source
+Platform/FreeRtos/Interface
+Platform/FreeRtos/Source
+Platform/FatFs/Interface
+Platform/FatFs/Source
+# Plus, located in YOUR project: the upstream library include dirs (lwIP,
+# Mbed TLS, FreeRTOS, FatFs) and the directory holding your config headers.
+
+## Required defines
+
+-DSOLIDSYSLOG_USER_TUNABLES_FILE="my_tunables.h" # your tunable overrides (optional)
+-DLWIP_DNS=1 # required by SolidSyslog::LwipRawDnsResolver
+# Mbed TLS: point MBEDTLS_USER_CONFIG_FILE (or MBEDTLS_CONFIG_FILE) at your mbedtls config,
+# and use the SAME config when building the mbedTLS library itself.
+
+## Integrator-supplied config headers
+
+lwipopts.h (LwipRaw)
+lwipopts.h (with LWIP_DNS=1) (LwipRawDnsResolver)
+mbedtls_config.h (MbedTls)
+FreeRTOSConfig.h (FreeRtos)
+ffconf.h (FatFs)
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 51fd4064..98befaf2 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -174,111 +174,60 @@ SolidSyslog integration is **three things**:
> `Platform//Source` on the include path for the `FooPrivate.h` header.
> The matrix above lists which groups you need per role.
-> *(This manifest is hand-authored against the current tree. S30.03 will
-> generate it from CMake so it can never drift — see #569.)*
-
---
## Worked manifest — the beta stack
**Target:** FreeRTOS + lwIP + Mbed TLS + FatFs, IAR, **no CMake**. TLS transport,
-store-and-forward, numeric (no-DNS) resolver, `NO_SYS=0`.
-
-### 1. Source files to compile
-
-**Core (all of it):**
-
-```
-Core/Source/*.c
-```
-
-> All 63 Core `.c` files. The sender group
-> (`SolidSyslogUdpSender*`, `SolidSyslogStreamSender*`,
-> `SolidSyslogSwitchingSender*`), the resolver/datagram dispatchers, and the
-> atomic-counter dispatcher (`SolidSyslogAtomicCounter*`) are part of Core; CMake
-> gates them by host probe, but a non-CMake build simply compiles the whole
-> directory — unused objects are dead-stripped by the linker and every role has a
-> Null fallback.
-
-**Network — lwIP (numeric resolver; DNS omitted so no `LWIP_DNS` needed):**
-
-```
-Platform/LwipRaw/Source/SolidSyslogLwipRawAddress.c
-Platform/LwipRaw/Source/SolidSyslogLwipRawAddressStatic.c
-Platform/LwipRaw/Source/SolidSyslogLwipRawDatagram.c # only if you send UDP
-Platform/LwipRaw/Source/SolidSyslogLwipRawDatagramStatic.c # only if you send UDP
-Platform/LwipRaw/Source/SolidSyslogLwipRawTcpStream.c
-Platform/LwipRaw/Source/SolidSyslogLwipRawTcpStreamStatic.c
-Platform/LwipRaw/Source/SolidSyslogLwipRawResolver.c
-Platform/LwipRaw/Source/SolidSyslogLwipRawResolverStatic.c
-Platform/LwipRaw/Source/SolidSyslogLwipRawMarshal.c # NO_SYS=0 only
-```
-
-> Omit `SolidSyslogLwipRawDnsResolver*` (that is what keeps `LWIP_DNS` off the
-> requirement list).
+store-and-forward, numeric resolver + DNS, `NO_SYS=0`.
-**TLS — Mbed TLS (over the lwIP TCP stream):**
+### 1. Source files + include directories — generated, not hand-listed
-```
-Platform/MbedTls/Source/SolidSyslogMbedTlsStream.c
-Platform/MbedTls/Source/SolidSyslogMbedTlsStreamStatic.c
-```
-
-> Optional at-rest integrity: add
-> `SolidSyslogMbedTlsHmacSha256Policy*` and/or `SolidSyslogMbedTlsAesGcmPolicy*`.
+The exact `.c` file list and SolidSyslog-side include directories for this stack
+are **generated from CMake** and committed, so they can never drift from what the
+packs actually ship (CI regenerates and fails on any difference):
-**OS — FreeRTOS:**
-
-```
-Platform/FreeRtos/Source/SolidSyslogFreeRtosMutex.c
-Platform/FreeRtos/Source/SolidSyslogFreeRtosMutexStatic.c
-Platform/FreeRtos/Source/SolidSyslogFreeRtosSysUpTime.c
-```
+**→ [`docs/generated/beta-stack-manifest.txt`](generated/beta-stack-manifest.txt)**
-**Store — FatFs (crash-safe persistent store-and-forward):**
+That file is the authoritative source/include/`-D`/config-header list — copy it
+straight into your IDE or Makefile. To generate the manifest for a **different**
+selection of packs, configure with your upstream trees on the environment and
+your pack list, then build the `manifest` target:
-```
-Platform/FatFs/Source/SolidSyslogFatFsFile.c
-Platform/FatFs/Source/SolidSyslogFatFsFileStatic.c
+```bash
+cmake -S . -B build/manifest \
+ -DSOLIDSYSLOG_MANIFEST_PACKS="LwipRaw;LwipRawDnsResolver;MbedTls;FreeRtos;FatFs"
+cmake --build build/manifest --target manifest # prints the manifest
```
-> Plus your own `diskio.c` media driver (FatFs is RTOS-agnostic; you supply the
-> block device). `BlockStore` / `RecordStore` / `BlockSequence` /
-> `FileBlockDevice` are already in the Core set above.
+Leave `SOLIDSYSLOG_MANIFEST_PACKS` empty to include every pack the configure
+defined. The Core `.c` set is always included; the host Pattern-A adapters
+(POSIX / Windows / OpenSSL / C11 atomics) are CMake-auto-detected host
+conveniences and are intentionally omitted from the (embedded) manifest.
-### 2. Include directories
+> The manifest lists the SolidSyslog-side include dirs only. You still add your
+> own upstream include dirs (lwIP, Mbed TLS, FreeRTOS, FatFs) and the directory
+> holding your config headers + `my_tunables.h`. FatFs also needs your own
+> `diskio.c` media driver.
-```
-Core/Interface
-Core/Source
-Platform/LwipRaw/Interface
-Platform/LwipRaw/Source
-Platform/MbedTls/Interface
-Platform/MbedTls/Source
-Platform/FreeRtos/Interface
-Platform/FreeRtos/Source
-Platform/FatFs/Interface
-Platform/FatFs/Source
-
-
-
-
-
-```
+### 2. Defines
-### 3. Defines
+The generated manifest's *Required defines* section is authoritative. For this
+stack:
-```
+```text
-DSOLIDSYSLOG_USER_TUNABLES_FILE="my_tunables.h" # your tunable overrides
+-DLWIP_DNS=1 # required by SolidSyslog::LwipRawDnsResolver
```
-> No `LWIP_DNS` is required for this stack (numeric resolver). The header-configured
-> upstreams take their settings from your config headers, not from `-D`s:
-> `lwipopts.h` (incl. `NO_SYS`, `LWIP_RAW`/`UDP`/`TCP`), `mbedtls_config.h`,
-> `FreeRTOSConfig.h` (with `configSUPPORT_STATIC_ALLOCATION=1` for the mutex),
-> `ffconf.h`.
+> `LWIP_DNS=1` is required because this stack includes the lwIP DNS resolver; a
+> numeric-only build (omit `SolidSyslog::LwipRawDnsResolver`) does not need it.
+> The header-configured upstreams take their other settings from your config
+> headers, not from `-D`s: `lwipopts.h` (incl. `NO_SYS`, `LWIP_RAW`/`UDP`/`TCP`),
+> `mbedtls_config.h`, `FreeRTOSConfig.h` (with
+> `configSUPPORT_STATIC_ALLOCATION=1` for the mutex), `ffconf.h`.
-### 4. Config headers you own
+### 3. Config headers you own
| Header | Owns |
|---|---|
@@ -288,7 +237,7 @@ Platform/FatFs/Source
| `ffconf.h` | FatFs feature set |
| `my_tunables.h` | SolidSyslog pool sizes / limits (see below) |
-### 5. Bring-your-own callbacks for this stack
+### 4. Bring-your-own callbacks for this stack
- **Sleep** — required by Mbed TLS (handshake retry) and the lwIP TCP stream
(bounded synchronous open). Wrap `vTaskDelay`.
@@ -310,7 +259,7 @@ library. Two equivalent mechanisms (works the same for CMake and non-CMake):
- **A whole file of overrides:**
- ```
+ ```text
-DSOLIDSYSLOG_USER_TUNABLES_FILE="my_tunables.h"
```
@@ -319,7 +268,7 @@ library. Two equivalent mechanisms (works the same for CMake and non-CMake):
- **Per-value on the command line:**
- ```
+ ```text
-DSOLIDSYSLOG_MAX_MESSAGE_SIZE=1024
```