Skip to content

feat: send over TLS instead of plain TCP - #43

Merged
DavidCozens merged 1 commit into
mainfrom
stage/tls
Jul 30, 2026
Merged

feat: send over TLS instead of plain TCP#43
DavidCozens merged 1 commit into
mainfrom
stage/tls

Conversation

@DavidCozens

@DavidCozens DavidCozens commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Pull request

What this stage adds

An mbedTLS stream wrapping the lwIP TCP stream, and the collector moves to 6514.
Server authentication only: the device verifies the collector against the trust
anchor it already holds and presents nothing of its own.

Flash +13,076 B (+688 on the previous stage)
RAM +35,664 B (+28,280)
mbedTLS peak +14,724 B (36,056 absolute)
Log stack +712 B (unchanged)
Service +3,800 B (+2,808)

Flash is the number worth reading. Adding TLS to this device costs well under a
kilobyte, not the ~150 KB mbedTLS occupies, because a device that already speaks
mTLS was carrying mbedTLS long before SolidSyslog arrived. What is measured here is
the code that drives TLS, not TLS itself. It is also smaller than it would have been
as a single hop from UDP: the TCP stream underneath was already linked and paid for.

The RAM accounts for itself exactly, and only 632 bytes of it is the library:

mbedTLS pool, 32 -> 53 KiB +21,504
service seam, 2 -> 8 KiB +6,144
stream and session objects +632
=+28,280

Both of those resizes were forced by the device, in that order, and both failed
loudly first. The pool was sized for the broker session alone, so the handshake
could not allocate: "MbedTlsStream category 0x0402", eighty-four times, and nothing
reached the collector. Given room, the handshake then overflowed the service seam:
"FATAL: stack overflow in task service", which takes the device down before it can
report, so neither figure can be read from the run that fails.

Both are then sized from a run that completes, by the rules the baseline already
uses. The pool is the measured peak times 1.5 rounded up to the next KiB — 36,092
gives 53 KiB, and the margin is fragmentation headroom rather than spare capacity,
because buffer_alloc hands out contiguous space. The seam is twice the measured
high-water of 3,852, and configMINIMAL_STACK_SIZE * 15 falls 24 bytes short of that,
so it takes 16. A peak that moves ~100 bytes run to run can put the first rule
either side of a KiB boundary; 53 or 54 both hold it.

The service stack carries the handshake, which is why it nearly quadruples while the
log stack does not move at all. A task that calls Log is unaffected by the transport
underneath it.

The TLS stream takes the trust anchor and DRBG as handles at create time, not paths
or PEM, so SimulatedExistingApp_StartCrypto() has to run before the scheduler. The
drain window covers a connect and handshake rather than a datagram, sized against
the library's 5 s handshake budget.

Files

 CMakeLists.txt          |  4 ++--
 README.md               | 14 +++++++++-----
 app/AppConfig.h         |  4 ++--
 app/main.c              |  3 ++-
 app/syslog/Syslog.c     | 20 ++++++++++++++++----
 measurements/stages.tsv |  1 +
 measurements/tls.csv    | 13 +++++++++++++
 run-report.md           | 34 +++++++++++++++++-----------------
 8 files changed, 62 insertions(+), 31 deletions(-)

Checklist

  • The diff touches build infra (1 file(s)) — see Files above; deliberate for this stage.
  • measurements/tls.csv committed.
  • Row added to measurements/stages.tsv.
  • README regenerated: python3 scripts/gen-cost-table.py.
  • ./run.sh green (build + QEMU + baseline self-check) — run-report.md committed.

Context for review: 16 of 21 sequential single-commit PRs replaying the integration on top of the Baseline root. Each lands green and reviewed before the next is built on it, because a change to an early commit would force every commit above it to be re-run and re-measured.

Summary by CodeRabbit

  • New Features

    • Added encrypted TLS transport for syslog records.
    • Added authenticated collector verification using the device’s trusted certificate authority.
    • Updated collector communication to use the standard TLS syslog port.
  • Documentation

    • Updated setup and stage documentation to describe TLS behavior and resource costs.
    • Refreshed run reports and validation results with TLS-specific measurements.
  • Performance

    • Increased available application memory and service task capacity to support TLS negotiation.

An mbedTLS stream wrapping the lwIP TCP stream, and the collector moves to 6514.
Server authentication only: the device verifies the collector against the trust
anchor it already holds and presents nothing of its own.

  Flash        +13,076 B    (+688 on the previous stage)
  RAM          +35,664 B      (+28,280)
  mbedTLS peak +14,724 B  (36,056 absolute)
  Log stack       +712 B     (unchanged)
  Service       +3,800 B       (+2,808)

Flash is the number worth reading. Adding TLS to this device costs well under a
kilobyte, not the ~150 KB mbedTLS occupies, because a device that already speaks
mTLS was carrying mbedTLS long before SolidSyslog arrived. What is measured here is
the code that drives TLS, not TLS itself. It is also smaller than it would have been
as a single hop from UDP: the TCP stream underneath was already linked and paid for.

The RAM accounts for itself exactly, and only 632 bytes of it is the library:

  mbedTLS pool, 32 -> 53 KiB   +21,504
  service seam, 2 -> 8 KiB      +6,144
  stream and session objects       +632
                               =+28,280

Both of those resizes were forced by the device, in that order, and both failed
loudly first. The pool was sized for the broker session alone, so the handshake
could not allocate: "MbedTlsStream category 0x0402", eighty-four times, and nothing
reached the collector. Given room, the handshake then overflowed the service seam:
"FATAL: stack overflow in task service", which takes the device down before it can
report, so neither figure can be read from the run that fails.

Both are then sized from a run that completes, by the rules the baseline already
uses. The pool is the measured peak times 1.5 rounded up to the next KiB — 36,092
gives 53 KiB, and the margin is fragmentation headroom rather than spare capacity,
because buffer_alloc hands out contiguous space. The seam is twice the measured
high-water of 3,852, and configMINIMAL_STACK_SIZE * 15 falls 24 bytes short of that,
so it takes 16. A peak that moves ~100 bytes run to run can put the first rule
either side of a KiB boundary; 53 or 54 both hold it.

The service stack carries the handshake, which is why it nearly quadruples while the
log stack does not move at all. A task that calls Log is unaffected by the transport
underneath it.

The TLS stream takes the trust anchor and DRBG as handles at create time, not paths
or PEM, so SimulatedExistingApp_StartCrypto() has to run before the scheduler. The
drain window covers a connect and handshake rather than a datagram, sized against
the library's 5 s handshake budget.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The example enables SolidSyslog MbedTLS support, configures authenticated TLS syslog delivery on port 6514, increases TLS-related memory and timing allocations, and updates README and run-report measurements for the TLS stage.

Changes

TLS syslog integration

Layer / File(s) Summary
Build and resource configuration
CMakeLists.txt, app/AppConfig.h
Enables the SolidSyslog MbedTLS platform and target, and increases the service stack and simulated MbedTLS heap allocations.
TLS transport and timing
app/syslog/Syslog.c, app/main.c
Replaces raw TCP with an MbedTLS stream using the device CA chain, RNG, and collector server name on port 6514; the harness waits 3 seconds after emitting a record.
TLS documentation and measurements
README.md, run-report.md
Documents the TLS stage and updates cost, size, runtime, and self-check measurements.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HarnessTask
  participant Syslog_Start
  participant SolidSyslogMbedTlsStream
  participant Collector
  HarnessTask->>Syslog_Start: Emit BOOT record
  Syslog_Start->>SolidSyslogMbedTlsStream: Configure TLS with CA chain, RNG, and ServerName
  SolidSyslogMbedTlsStream->>Collector: Connect securely on port 6514
  SolidSyslogMbedTlsStream-->>Collector: Send encrypted syslog record
  HarnessTask->>HarnessTask: Wait 3000 ms before measurement
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: switching syslog transmission from plain TCP to TLS.
Description check ✅ Passed The description follows the required template and includes the stage summary plus the checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stage/tls

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/main.c (1)

73-74: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Wait for a real drain/completion signal
LogTask_EmitOnce() only waits for the record to be queued; ServiceTask does the actual SolidSyslog_Service() drain/send loop. Replace the fixed 3-second sleep before Measure_Report() with an explicit service-complete/error signal so the report can’t run before the TLS send finishes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/main.c` around lines 73 - 74, Replace the fixed vTaskDelay in
LogTask_EmitOnce() with synchronization on an explicit ServiceTask
completion/error signal emitted after SolidSyslog_Service() finishes draining or
fails. Make LogTask_EmitOnce() wait for that signal before calling
Measure_Report(), preserving error propagation and preventing the report from
running before the TLS send completes.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/main.c`:
- Around line 73-74: Replace the fixed vTaskDelay in LogTask_EmitOnce() with
synchronization on an explicit ServiceTask completion/error signal emitted after
SolidSyslog_Service() finishes draining or fails. Make LogTask_EmitOnce() wait
for that signal before calling Measure_Report(), preserving error propagation
and preventing the report from running before the TLS send completes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13bd48cb-9b2b-490f-99a5-2e85127f8b5e

📥 Commits

Reviewing files that changed from the base of the PR and between 23bfd9c and 7fc4655.

⛔ Files ignored due to path filters (2)
  • measurements/stages.tsv is excluded by !**/*.tsv
  • measurements/tls.csv is excluded by !**/*.csv
📒 Files selected for processing (6)
  • CMakeLists.txt
  • README.md
  • app/AppConfig.h
  • app/main.c
  • app/syslog/Syslog.c
  • run-report.md

@DavidCozens
DavidCozens merged commit a079767 into main Jul 30, 2026
2 checks passed
@DavidCozens
DavidCozens deleted the stage/tls branch July 30, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant