Skip to content
Merged
115 changes: 87 additions & 28 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,19 @@ include them), run `addSubIssue` retroactively; it's idempotent-safe on closed i
## Project Board Membership

The `SolidSyslog` project board (`gh project list --owner DavidCozens` → project 1) has a
`Status` single-select field with options **Todo**, **In Progress**, **Done**. Adding an
issue to the repo does **not** add it to the board — that is a separate step and must be
done explicitly.
`Status` single-select field with options **Todo**, **In Progress**, **Done**.

Project workflows keep membership and status; there is no manual step. Linking a story
under its epic with `addSubIssue` puts it on the board at `Todo`, opening a pull request
that links the issue moves it to `In Progress`, and closing it sets `Done`. The workflows
add nothing that has no parent, so a chore or docs issue raised without an epic stays off
the board; a few early items predate them.

Confirm any of that by reading the board rather than the workflow list — an automation
being enabled says nothing about which field it writes, and the API exposes each
workflow's name and enabled flag but not its action. Pass
`archivedStates: [ARCHIVED, NOT_ARCHIVED]` when you read: `projectV2.items` omits
archived items by default, so a board read without it is a partial one.

### Convention

Expand All @@ -124,32 +134,39 @@ done explicitly.
housekeeping step, not a status transition. Archived items stay on the project and still
count in the epic's sub-issue roll-up.

### Add-to-board recipe
### Repairing board state by hand

Nothing routine needs this — the workflows above place items and set status. It is here
for the case where one has not fired, or a status is wrong and needs correcting.

```bash
# Project and Status field IDs (stable for this repo):
# projectId = PVT_kwHOAPhEnM4BTETq
# statusField = PVTSSF_lAHOAPhEnM4BTETqzhAat7w
# options = Todo:f75ad846 In Progress:47fc9ee4 Done:98236657

# 1. Get the issue's node ID.
gh api graphql -f query='
query {
repository(owner: "cososo-ltd", name: "solid-syslog") {
issue(number: <N>) { id }
# Read the board. `issue.projectItems` returns 0 here -- the project is user-owned and
# the repository org-owned -- so enumerate the project's items and paginate past 100.
# archivedStates is required: without it the archived items are silently missing.
gh api graphql --paginate -f query='
query($endCursor: String) {
user(login: "DavidCozens") {
projectV2(number: 1) {
items(first: 100, after: $endCursor, archivedStates: [ARCHIVED, NOT_ARCHIVED]) {
pageInfo { hasNextPage endCursor }
nodes {
id
content { ... on Issue { number state } }
fieldValueByName(name: "Status") {
... on ProjectV2ItemFieldSingleSelectValue { name }
}
}
}
}
}
}'

# 2. Add to project (returns the new project item's id).
gh api graphql -f query='
mutation {
addProjectV2ItemById(input: {
projectId: "PVT_kwHOAPhEnM4BTETq",
contentId: "<ISSUE_NODE_ID>"
}) { item { id } }
}'

# 3. Set Status (use the option id matching Todo / In Progress / Done).
# Correct a status, using the item id the query above returns.
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(input: {
Expand Down Expand Up @@ -203,21 +220,21 @@ For every new story:
Pad both the epic and story numbers to two digits.
2. `addSubIssue` it under the parent epic (see **Issue / Epic Linking** above). The
Parent-issue link is what groups the story into the correct swimlane.
3. Add the story to the project board with `Status = Todo` using the recipe above. Do
**not** add the parent epic itself — the swimlane appears automatically once a child
story is on the board.

There is no third step. Step 2 puts the story on the board at `Todo`, and the swimlane
appears with it. Do **not** add the parent epic — it is not an item.

### Work-in-progress limit

`In Progress` holds at most two items — typically one functional story and one BDD
story. **Check the count before moving anything into `In Progress`, and say so if the
move would make it three.** The limit is only worth having if someone is watching it,
and the board does not enforce it.
story. A linked pull request is what moves a story there, so this is in practice a limit
on open PRs: **check the count before opening one that would make it three, and say so.**
The limit is only worth having if someone is watching it, and the board does not enforce
it.

### When closing a story

When a PR merges and closes a story, flip its project Status to `Done`. Closing the
issue does not update the Status field automatically.
A merged PR that closes the story sets `Done` for you.

Epics don't have a Status field on the board (they aren't items). When every child
story is Done, the swimlane naturally becomes all-Done; the epic issue itself should
Expand Down Expand Up @@ -429,7 +446,7 @@ why.** Read the header rather than a copy of it. For the wider map:

- `docs/roles/index.md` — the roles, each with its vtable contract and the backends
that realise it.
- `docs/platforms/*.md` — what each platform pack supplies.
- `docs/platforms/<slug>/index.md` — what each platform supplies.
- `docs/api-reference/` plus the generated Doxygen indexes (`docs/api/files.md`,
`annotated.md`, `functions.md`, `macros.md`) — every header and symbol.

Expand Down Expand Up @@ -493,6 +510,31 @@ Deliberate deviations from the MISRA rule set are recorded in
- cppcheck runs with `--error-exitcode=1`. Inline suppressions (`// cppcheck-suppress`) must include
a comment explaining why.

### Characters in source

Write what a UK keyboard types. In `.c`, `.h` and `.cpp` — comments included — that
means `-` and not an em or en dash, `...` and not an ellipsis, `->` and not an arrow,
and the ASCII spelling of a symbol: `<=`, `+/-`, `x`, `||`, "sum of". A continuation
ellipsis takes no leading comma: `first, second...`, not `first, second, ...`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Three exceptions stand, each authorised rather than assumed:

- `µ` in a unit, and `§` in an RFC citation. Both read better as themselves and
neither is ambiguous.
- A character that *is* the subject of its comment. The UTF-8 tests name U+00E9, the
euro sign and an emoji to explain the byte sequences they encode; replacing them
would delete the point.
- String literals were left alone in the sweep that established this. Seven carry an
em dash and are runtime or test output, where the character is data rather than
typography.

**Anything else non-ASCII: ask.** Do not quietly pick a typographic character, and do
not quietly rewrite a sentence to avoid one — either way the decision goes unrecorded.

This is a rule about source. Documentation under `docs/` keeps typographic characters,
on the reasoning that prose of that length is written with an authoring tool; `README.md`
is hyphenated by hand and is the deliberate exception.

### MISRA-load-bearing `.clang-format` settings

Two settings in `.clang-format` are not merely stylistic — they enforce MISRA C:2012 rules at
Expand Down Expand Up @@ -563,6 +605,23 @@ comes from, couples the two: the eleventh platform then has to be added to ten
pages. State this platform's own behaviour completely, and point at the
capability matrix in `docs/platforms/index.md` for who fills what.

### Link the record, not the source

A documentation page does not send the reader into the source tree. Where a
symbol has a generated API page, link that; otherwise name the file in code font
and leave it there. The page's job is to say what the library does, not to show
where it is implemented.

Issues and pull requests are the opposite case, and are linked. They are the
tracking record, and a reader who has just been told that a platform diverges
from a contract wants to see whether that is still true.

The repository-root documents are outside this rule rather than an exception to
it. `README.md`, `SECURITY.md`, `SUPPORT.md` and `LICENSE.md` are read on GitHub
as well as published into the site by `hooks/root_pages.py`, so their links stay
repo-relative; `hooks/source_links.py` rewrites whatever escapes `docs/` to a
canonical URL at build time.

---

## Design Patterns
Expand Down
77 changes: 39 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

A structured syslog client library for embedded and industrial systems, implementing
RFC 5424 (structured syslog) with RFC 5426 (UDP) and RFC 6587 (TCP) transports.
TLS per RFC 5425 ships two ways — `SolidSyslogOpenSslStream` over OpenSSL and
`SolidSyslogMbedTlsStream` over Mbed TLS — and any other TLS library (wolfSSL,
hardware offload, …) plugs in behind the same Stream vtable. TLS is not a core
dependency: Core carries no reference to any TLS library.
TLS per RFC 5425 is supplied by a platform, so any TLS library plugs in behind the
same Stream vtable. TLS is not a core dependency: Core carries no reference to any
TLS library.

It exists to give shipping embedded products the security audit trail the EU
Cyber Resilience Act and IEC 62443 expect — as a component you add, not a
redesign.
Cyber Resilience Act and IEC 62443 expect: a component you add, not a redesign.

## What it costs

**+5 KB flash** for a valid, timestamped RFC 5424 record on the wire, and **0.4 KB
of RAM**. **+13.5 KB flash** for the whole path — store-and-forward, device identity,
mutual TLS and AES-GCM encryption at rest — and **37 KB of RAM**, mainly TLS buffers.
Figures measured on FreeRTOS with lwIP on a Cortex-M3, run under QEMU — a
representative device, not a specification.
of RAM**. **+13.5 KB flash** for the whole path - store-and-forward, device identity,
mutual TLS and AES-GCM encryption at rest - and **37 KB of RAM**, mainly TLS buffers.
Each figure is what SolidSyslog adds to a device already running FreeRTOS, lwIP,
FatFs and Mbed TLS: a baseline built to carry the third-party code a real-world
device would already have. Measured on a Cortex-M3 under QEMU, a representative
device rather than a specification.

Both are measured by a worked integration, published in full as
[solid-syslog-example](https://github.com/cososo-ltd/solid-syslog-example) (consumed
Expand All @@ -28,28 +28,28 @@ gives the diff and the measured cost.

Designed for resource-constrained environments:

- C99, no dynamic memory allocation every instance lives in a library-internal static pool, sized at compile time
- Transport-agnostic UDP, TCP, TLS, or bring your own
- Buffer-agnostic PassthroughBuffer (direct send), portable CircularBuffer (mutex-injected ring), POSIX message queue, or bring your own
- C99, no dynamic memory allocation - every instance lives in a library-internal static pool, sized at compile time
- Transport-agnostic - UDP, TCP, TLS, or bring your own
- Buffer-agnostic - PassthroughBuffer (direct send), portable CircularBuffer (mutex-injected ring), POSIX message queue, or bring your own
- MISRA C:2012 informed

## Capabilities

RFC 5424 structured formatting over UDP (RFC 5426), TCP (RFC 6587), and TLS /
mutual TLS (RFC 5425). Asynchronous buffering, rotating block store-and-forward,
and at-rest record protection CRC-16 for accidental corruption, or keyed
and at-rest record protection: CRC-16 for accidental corruption, or keyed
HMAC-SHA256 / AES-256-GCM where a local attacker is in scope. The audit-logging
capabilities an IEC 62443 deployment draws on are mapped control by control in the
[IEC 62443 guide](https://docs.cososo.co.uk/solid-syslog/iec62443/).

SolidSyslog is built for embedded and RTOS targets. Every platform dependency
TCP/IP stack, TLS library, filesystem, OS primitives, clock is injected through
SolidSyslog is built for embedded and RTOS targets. Every platform dependency -
TCP/IP stack, TLS library, filesystem, OS primitives, clock - is injected through
a vtable, so the library ports to an embedded OS by filling roles rather
than editing Core. Reference adapters ship for FreeRTOS on Cortex-M (networking
via FreeRTOS-Plus-TCP or lwIP, transport security via `SolidSyslogMbedTlsStream`
over Mbed TLS, persistent store-and-forward over ChaN FatFs or FreeRTOS-Plus-FAT)
— and for POSIX and Windows, fully supported as development, test, and edge /
gateway hosts. Bring your own stack and the same Core runs unchanged.
than editing Core. Platforms ship for FreeRTOS on Cortex-M, and for POSIX and
Windows, fully supported as development, test, and edge / gateway hosts. Which
upstream fills which capability is in the
[platform matrix](https://docs.cososo.co.uk/solid-syslog/platforms/). Bring your
own stack and the same Core runs unchanged.

TLS revocation (CRL / OCSP) is not performed by the library. Whether it is
enforced depends on the TLS backend and platform you configure.
Expand All @@ -58,7 +58,7 @@ enforced depends on the TLS backend and platform you configure.

Full documentation lives at
[docs.cososo.co.uk/solid-syslog](https://docs.cososo.co.uk/solid-syslog/), organised
around what you came to do: **Overview**, **Adopt**, **Port a new platform**,
around what you came to do: **Core**, **Integrate**, **Platforms**,
**Compliance**, **API reference**, and **Maintaining**. New here?
[Compliance in one page](https://docs.cososo.co.uk/solid-syslog/overview/) is the
fastest orientation for evaluators.
Expand All @@ -71,15 +71,15 @@ time, stating what each stage adds, the question that decides whether you need i
and an indication of what it costs.

[Adding it to your build](https://docs.cososo.co.uk/solid-syslog/build-integration/)
is the build detail behind it: the capability matrix, the three ways to consume the
library — CMake, Make, and a source manifest for an IDE project — and the
compile-time tunables.
is the build detail behind it: how the library composes, how to pick your stack, the
three ways to consume it - CMake, Make, and a source manifest for an IDE project -
and the compile-time tunables.

## Building and testing

Developing the library itself? See
[Building and testing](https://docs.cososo.co.uk/solid-syslog/builds/) the
contributor/maintainer preset catalogue alongside the pre-PR check budget, the BDD
[Building and testing](https://docs.cososo.co.uk/solid-syslog/builds/) - the
contributor/maintainer preset catalogue - alongside the pre-PR check budget, the BDD
infrastructure, the CI pipeline, and the container images, all under **Maintaining**
on the documentation site. (Consuming the library in your product is the integration
path above.)
Expand All @@ -88,7 +88,7 @@ path above.)

SolidSyslog is OO-in-C. Every platform dependency and every optional feature is a
vtable role, injected at setup and composed at link time, so a feature you do not
wire is dropped by the linker rather than excluded by the preprocessor Core's
wire is dropped by the linker rather than excluded by the preprocessor. Core's
implementation contains no conditional compilation at all. Public headers are split
by audience: application code that logs events includes `SolidSyslog.h` and nothing
else, while the setup that builds a logger includes `SolidSyslogConfig.h` plus one
Expand All @@ -100,17 +100,18 @@ that split and links the generated reference for every header, type and symbol.
the anatomy of an adapter, and the Null object that stands in for any role you
leave unfilled.

[`Bdd/Targets/`](Bdd/Targets/) holds one BDD-driven binary per platform — Linux,
Windows, and FreeRTOS on QEMU — each exercising the library end to end against a real
syslog server; see [BDD testing](https://docs.cososo.co.uk/solid-syslog/bdd/).
[`Bdd/Targets/`](Bdd/Targets/) holds one BDD-driven binary per platform - Linux,
Windows, and two FreeRTOS-on-QEMU builds, one per network stack - each exercising
the library end to end against a real syslog server; see
[BDD testing](https://docs.cososo.co.uk/solid-syslog/bdd/).

## Compliance

- [CRA guide](https://docs.cososo.co.uk/solid-syslog/cra/) the Annex I map: the requirement that names logging, the requirements an audit trail contributes to, what the project publishes for your vulnerability handling, and the dates the Regulation applies from
- [Compliance in one page](https://docs.cososo.co.uk/solid-syslog/overview/) the evaluator's one-screen orientation on CRA and IEC 62443
- [IEC 62443 compliance guide](https://docs.cososo.co.uk/solid-syslog/iec62443/) the audit-logging-relevant controls, and what the library provides against each
- [RFC compliance matrix](https://docs.cososo.co.uk/solid-syslog/rfc-compliance/) sender-side coverage of RFC 5424, 5426, 6587, and 5425
- [Threat model](https://docs.cososo.co.uk/solid-syslog/security/threat-model/) the division of responsibility between the library and your product
- [CRA guide](https://docs.cososo.co.uk/solid-syslog/cra/) - the Annex I map: the requirement that names logging, the requirements an audit trail contributes to, what the project publishes for your vulnerability handling, and the dates the Regulation applies from
- [Compliance in one page](https://docs.cososo.co.uk/solid-syslog/overview/) - the evaluator's one-screen orientation on CRA and IEC 62443
- [IEC 62443 compliance guide](https://docs.cososo.co.uk/solid-syslog/iec62443/) - the audit-logging-relevant controls, and what the library provides against each
- [RFC compliance matrix](https://docs.cososo.co.uk/solid-syslog/rfc-compliance/) - sender-side coverage of RFC 5424, 5426, 6587, and 5425
- [Threat model](https://docs.cososo.co.uk/solid-syslog/security/threat-model/) - the division of responsibility between the library and your product

Reporting a vulnerability: [`SECURITY.md`](SECURITY.md).

Expand All @@ -121,7 +122,7 @@ Copyright 2026 Cozens Software Solutions Limited.
Licensed under the [PolyForm Noncommercial License 1.0.0](LICENSE.md). Free for
noncommercial, personal, educational, and government use.

For commercial licensing including pricing, the early-adopter programme, and
the platform adapter policy see the
For commercial licensing - including pricing, the early-adopter programme, and
the platform adapter policy - see the
[SolidSyslog product page](https://www.cososo.co.uk/products/solid-syslog/), or
use the contact form at [cososo.co.uk](https://www.cososo.co.uk/#contact).
10 changes: 3 additions & 7 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@ series by Cozens Software Solutions Limited.

This file holds **how we work together**. [`CLAUDE.md`](CLAUDE.md) holds **how the
repository works** — git and issue workflow, project structure, naming, code style,
design patterns, and the board and milestone conventions. Neither restates the other.

When two sources disagree, the repository wins: `.clang-format`, `.clang-tidy`,
`.markdownlint-cli2.jsonc`, `CMakePresets.json` and `.github/workflows/ci.yml` are
executable and cannot drift from what actually happens. A briefing in conversation
outranks both files — it is the most recent intent — but say so when it contradicts a
file, so the file gets fixed rather than quietly bypassed.
design patterns, and the board and milestone conventions. Neither restates the other,
precedence included: what wins when two sources disagree is stated once, in CLAUDE.md
under **Precedence, when two sources disagree**.

## Collaboration modes

Expand Down
Loading
Loading