Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 25 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,42 @@
# solid-syslog-example

A worked integration of [SolidSyslog](https://github.com/cososo-ltd/solid-syslog), built up in
stages — from a device with no syslog at all to one whose records are authenticated and encrypted.
An example of integrating [SolidSyslog](https://github.com/cososo-ltd/solid-syslog) into an
application that already exists, starting from a realistic device rather than an empty one.

Each stage is one commit. It says what it does, what it changes, what it gives you, and what it
costs. The costs are measured by the device itself, not estimated.
Each pull request merged to `main` is one stage of that integration, and each raises the security
posture of the logging path. A stage states what it changes, what it gives you, and what it costs,
measured by the device itself rather than estimated.

It builds on a baseline that simulates the sort of device you might be adding this to, and that
measures itself: see [docs/baseline.md](docs/baseline.md) for what the baseline is, how the
figures are made, and how to run it.
The purpose is to show the process of integration, the cost of each step and the benefit it brings,
so that all three can be set against your own threat model when you plan your own.

## This stage — Right-sized
The baseline is the sort of device this might be added to: it already networks, mounts a filesystem
and holds a mutual-TLS session to its broker before any syslog exists. See
[docs/baseline.md](docs/baseline.md) for what it is, how the figures are made, and how to run it.

Fit the compile-time sizes to what this device uses, now that every collaborator is in place.
## Where it ends up

The message cap comes first, because the ring, the store's record buffer and the formatter frame on
both task stacks all follow it.

```c
/* app/config/solid_syslog_tunables.h */
#define SOLIDSYSLOG_MAX_MESSAGE_SIZE 400U

#define SOLIDSYSLOG_ADDRESS_POOL_SIZE 1U
#define SOLIDSYSLOG_TCP_STREAM_POOL_SIZE 1U
#define SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE 1U
```

The worst case measured here is 345 octets: the four SD-ELEMENTs with both counters at full 32-bit
width and both addresses at fifteen characters, plus a short message. 400 allows for longer messages
on this device. Anything longer is truncated rather than dropped.

The pool defaults suit a device running several transports at once. This one runs a single sender
over a single stream to a single destination.

The overrides reach the library through `SOLIDSYSLOG_USER_TUNABLES_FILE`, which the library carries
on an INTERFACE target, so Core, the platform packs and this application all compile against the
same values. They change struct sizes, and a build where only some translation units saw them would
disagree about how big those structs are.

The ring drops from eight records to four. The store holds a backlog, so the ring only has to absorb
what can be logged while the service task is sending.

The task stacks go last, at twice their measured high-water marks rounded up to a whole
`configMINIMAL_STACK_SIZE`.

**When you need it.** Once the pipeline is complete. Sizing earlier means sizing against a device
that is still missing collaborators.
The device logs one RFC 5424 record carrying four SD-ELEMENTs: sequence number and uptime, time
quality, origin, and a private element naming the protection its own log pipeline is under. The
record goes to the collector over mutual TLS and is spooled to a local store encrypted with
AES-256-GCM, so records survive a failed send and a disk that leaves the device gives nothing away.

<!-- STAGE-COST:START (generated by scripts/gen-cost-table.py — do not edit by hand) -->

**Cost above baseline: Flash +13,788 B, RAM +36,048 B.**

<!-- STAGE-COST:END -->

The full report for this stage — what the device did, every figure, and the self-check — is
committed as [`run-report.md`](run-report.md), and rewritten by every stage.
Most devices want less than that. The table below prices every stage, and the cheapest row that does
anything useful — a valid, timestamped record on the wire — is a fraction of it.

**Read it as a sequence.** Start at the Baseline commit and step forward. `git show` on any stage
gives the diff to apply to your own build, the reasoning behind it, and the measured cost of applying
it; each stage's run is committed alongside it as [`run-report.md`](run-report.md). Stop where your
device's threat model does.

This history is rebuilt against each SolidSyslog release, which means a force-push. An existing clone
or fork needs re-cloning rather than pulling.

## Every stage

Expand Down
12 changes: 6 additions & 6 deletions run-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
[report] flash_data,656,316,340
[report] static_bss,146584,110876,35708
[report] heap_used,4440,4440,0
[report] mbedtls_peak,37248,21332,15916
[report] mbedtls_free,19072,11436,7636
[report] mbedtls_peak,37156,21332,15824
[report] mbedtls_free,19164,11436,7728
[report] lwip_mem_free,7576,7576,0
[report] lwip_pbufs_free,13,14,-1
[report] stack_log,720,120,600
Expand Down Expand Up @@ -47,8 +47,8 @@
## Collector (syslog-ng) received

```text
wire <134>1 2026-08-16T16:16:14.430000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] device started
parsed PRIORITY=134 TIMESTAMP=2026-08-16T16:16:14+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] MSG=device started
wire <134>1 2026-08-16T17:28:13.500000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="250"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] device started
parsed PRIORITY=134 TIMESTAMP=2026-08-16T17:28:13+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="250"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] MSG=device started
```

## Self-check (vs measurements/right-size.csv)
Expand All @@ -58,8 +58,8 @@ parsed PRIORITY=134 TIMESTAMP=2026-08-16T16:16:14+00:00 HOSTNAME=10.0.2.15 APP_N
OK flash_data: 656 (expected 656, Δ0)
OK static_bss: 146584 (expected 146584, Δ0)
OK heap_used: 4440 (expected 4440, Δ0)
OK mbedtls_peak: 37248 (expected 37184, Δ64)
OK mbedtls_free: 19072 (expected 19136, Δ64)
OK mbedtls_peak: 37156 (expected 37184, Δ28)
OK mbedtls_free: 19164 (expected 19136, Δ28)
OK lwip_mem_free: 7576 (expected 7576, Δ0)
OK lwip_pbufs_free: 13 (expected 13, Δ0)
OK stack_log: 720 (expected 720, Δ0)
Expand Down
Loading