Skip to content
Open
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
56 changes: 56 additions & 0 deletions .github/workflows/raf-freestanding-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Isolated host-only experimental gate; never deploy, merge or release.
name: RAFAELIA freestanding evidence gate (scoped)
on:
pull_request:
paths:
- 'contrib/raf_freestanding_gate/**'
- '.github/workflows/raf-freestanding-gate.yml'
push:
branches:
- research/9251-freestanding-audit-gate-v1
paths:
- 'contrib/raf_freestanding_gate/**'
- '.github/workflows/raf-freestanding-gate.yml'

permissions:
contents: read

jobs:
adversarial-host-scoped:
runs-on: ubuntu-24.04
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
cc: [gcc, clang]
steps:
- name: Checkout pinned
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Compile freestanding core, zero external symbols
shell: bash
env:
CC: ${{ matrix.cc }}
run: |
set -euo pipefail
cd contrib/raf_freestanding_gate
"${CC}" -std=c11 -O2 -ffreestanding -fno-builtin \
-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Werror \
-Iinclude -c src/raf_gate.c -o "${RUNNER_TEMP}/raf_gate.o"
if [[ -n "$(nm -u "${RUNNER_TEMP}/raf_gate.o")" ]]; then
echo "Core has forbidden undefined symbols"; exit 1
fi
- name: Adversarial host tests (not Firebird E2E)
shell: bash
env:
CC: ${{ matrix.cc }}
run: |
set -euo pipefail
cd contrib/raf_freestanding_gate
"${CC}" -std=c11 -O1 -g -Wall -Wextra -Wpedantic \
-Wconversion -Wsign-conversion -Werror \
-fsanitize=address,undefined -fno-omit-frame-pointer \
-Iinclude src/raf_gate.c tests/test_raf_gate.c \
-o "${RUNNER_TEMP}/test_raf_gate"
"${RUNNER_TEMP}/test_raf_gate"
176 changes: 176 additions & 0 deletions contrib/raf_freestanding_gate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Freestanding evidence gate V1 — Firebird #9251 experimental companion

> Scope: **experimental, isolated and opt-in**, not part of the upstream fix,
> not a Firebird server plugin, not a certified standards implementation.
> The producer fix stays in [PR #9274](https://github.com/FirebirdSQL/firebird/pull/9274).
> Firebird 4/5 backports remain separate.

## Boundary and portability

- Core language: C11, `stddef.h` + `stdint.h`; one .c and one .h.
- No allocation, file/network calls, syscalls, globals with mutable state,
libc calls, exceptions, threading or host clock.
- All state changes and durable writes happen **outside** the core through
explicit callbacks. The host test harness does use libc; the **core does not**.
- Source files are offered under the InterBase Public License 1.0, preserving
the Firebird repository's existing licensing boundary. No external code
or third-party cryptographic library is copied into the core.
- No change to `src/remote/protocol.cpp`, existing Firebird build rules, ABI,
production logging or repository release branches in this experiment.

## Contract / typed states

```
request + nonempty evidence + parent digest + next sequence
|
v
preflight (read-only) ----fail----> BLOCKED receipt (if persisted)
|
v
atomic PREPARED receipt ----not definitely durable----> BLOCKED, no apply
|
v
apply -> verify ----------------success----> PASS terminal receipt
| |
| failure terminal persist definitely rejected
v |
rollback -> verify_recovery <--------------------+
| success
v
ROLLED_BACK receipt
| failure
v
failback -> verify_recovery -> FAILBACK receipt or UNRECOVERABLE
```

Return codes from `persist`: **1** = confirmed durable, **0** = definitely
not appended, **-1** = indeterminate. An indeterminate terminal append
sets `UNRECOVERABLE` pending human/host reconciliation; it is NOT a
declaration of physical machine damage. Under an uncertain outcome, the
core does not guess which state was stored or silently perform a second
potentially conflicting action.

**Mandatory adapter duties:**

1. Authorize the change and prove an independent known-good rollback state
before enabling `apply`; preflight MUST have no side effect.
2. `apply` MUST be reversible and idempotent. `rollback` and `failback`
MUST be idempotent and implement distinct recovery strategies. Do not
wrap irreversible production writes behind this API.
3. `verify_recovery` MUST check the **actual recovered/fallback state**,
not just repeat the recovery callback's return code.
4. `persist` MUST atomically compare-and-swap (sequence, previous hash)
at a durable append-only store; return 1 only after a readback-equivalent
durability guarantee. A callback that always returns 1 is a TEST STUB,
not a production adapter.
5. On crash after PREPARED, the host MUST reconcile in-flight effects and
emit an append-only successor. The core does not provide process restart,
power-loss recovery, concurrency control, or a watchdog.
6. The host MUST supply attributable, privacy-reviewed evidence bytes and
retain them securely. A digest without archived original bytes cannot
prove the meaning or authenticity of the evidence.
7. A SHA-256 chain detects accidental or unauthorized changes only when
compared with a trusted anchor. It is **not a digital signature**, identity
attestation, secure timestamp, or complete intrusion defense.
8. Never let a gate approve a deployment based solely on a host-level test.

## Receipt wire V1 (114 bytes, big-endian)

| Offset | Bytes | Field |
|---|---:|---|
| 0 | 4 | ASCII `RFG1` |
| 4 | 1 | format version = 1 |
| 5 | 8 | sequence |
| 13 | 1 | state |
| 14 | 32 | previous record SHA-256 |
| 46 | 4 | exact evidence byte count |
| 50 | 32 | SHA-256 of exact evidence bytes |
| 82 | 32 | SHA-256 of bytes [0,82) |

A successful transaction produces PREPARED + terminal records, linked by
the previous-digest field. `raf_receipt_valid` checks record integrity and
chain pointer; `raf_receipt_evidence_match` checks independently supplied
evidence against the stored digest. Internal format is a **custom versioned
binary schema**, not CBOR, JSON, RFC 8785 JCS or W3C PROV serialization.

## Standards mapping: target vs evidence

| Standard / framework | Applicable boundary | V1 result |
|---|---|---|
| RFC 6234 / SHA-256 | digest test vectors | Two known vectors + independent host tests; no FIPS certification |
| RFC 5424 | syslog header/message syntax | Bounded ASCII text formatter, using NILVALUE for missing clock/host; **NOT** complete syslog service |
| RFC 5425 (and updates) | secure syslog transport | **NOT IMPLEMENTED** — TLS is a host adapter responsibility |
| RFC 3339 | Internet timestamps | **NOT IMPLEMENTED** — host must supply verified time where available |
| RFC 8785 | canonical JSON | **NOT IMPLEMENTED** — receipts use binary schema |
| W3C PROV-O / PROV-DM | entities, activities, agent, derived evidence | **MAPPING PROPOSED**, exporter not implemented |
| IEEE 1012-2024 | V&V process traceability | Design intent only; formal V&V conformance not assessed |
| ISO/IEC/IEEE 29119-2:2021 | governed testing processes | Local adversarial cases, not a process conformance claim |
| 6 Sigma / DMAIC | improvement workflow | Method only; no DPMO measurement, capability index or certification |

Standards:
[RFC 5424](https://www.rfc-editor.org/rfc/rfc5424),
[RFC 5425](https://www.rfc-editor.org/rfc/rfc5425),
[RFC 6234](https://www.rfc-editor.org/rfc/rfc6234),
[RFC 3339](https://www.rfc-editor.org/rfc/rfc3339),
[RFC 8785](https://www.rfc-editor.org/rfc/rfc8785),
[W3C PROV-O](https://www.w3.org/TR/prov-o/),
[IEEE 1012](https://standards.ieee.org/ieee/1012/7324/),
[ISO/IEC/IEEE 29119-2](https://standards.ieee.org/ieee/2416/7498/).

A future PROV adapter can map record -> `prov:Entity`, verification
step -> `prov:Activity`, verified external authority -> `prov:Agent`,
and evidence lineage -> `prov:wasDerivedFrom`. No agent identity is
invented by the core.

## DMAIC and adversarial falsification

- **Define / ψ:** precise invariant and risk; for Firebird #9251,
continuation with zero BLOB ID MUST preserve the previously decoded kind.
- **Measure / χ:** source commit, current HEAD, failed packet-size case,
host capabilities and baseline.
- **Analyze / ρ:** model corruption, missing BPB, altered default flags,
ambiguous callbacks, timeouts, lost receipts and crash windows.
- **Improve / Δ:** smallest reversible change; freestanding gate stays
separate from the protocol hotfix.
- **Control / Σ → Ω → ψ':** run adverse cases, verify receipt digests, read
provider CI, freeze on uncertainty, and append superseding evidence.

The 21 C host cases include SHA-256 known vectors, chain-link validation,
evidence substitution, syslog size bounds, invalid/preflight-blocked input,
missing/indeterminate persistence, failed apply, failed verify, rollback,
independently verified failback, failed recovery verification, missing
callbacks and sequence overflow.

### Reproduce on host (tests only; not a production Firebird build)

```sh
cd contrib/raf_freestanding_gate
cc -std=c11 -O2 -ffreestanding -fno-builtin -Wall -Wextra -Wpedantic \
-Wconversion -Wsign-conversion -Werror -Iinclude -c src/raf_gate.c -o /tmp/raf_gate.o
nm -u /tmp/raf_gate.o # expected: no undefined symbols
cc -std=c11 -O1 -g -Wall -Wextra -Wpedantic -Wconversion \
-Wsign-conversion -Werror -fsanitize=address,undefined \
-fno-omit-frame-pointer -Iinclude src/raf_gate.c tests/test_raf_gate.c \
-o /tmp/test_raf_gate
/tmp/test_raf_gate
```

Locally scoped: GCC 14.2 and Clang 17 both compiled the isolated
freestanding object with zero undefined symbols and passed 21 host cases
under ASan/UBSan. This is **PASS_SCOPED_HOST**, not Firebird CI or E2E.

## Integration gate / rollback policy

This experiment MUST NOT be copied into Firebird's master PR as a broad
unreviewed refactor. A proposed adapter for `src/remote/protocol.cpp`
requires maintainer permission, a separately reviewed architecture,
a scope-specific ABI compatibility check, packet-boundary BLOB regression
tests on Firebird 4/5/master, actual CI receipts, and a documented
runtime rollback/failback procedure. Until then:

```text
SOURCE != ARTIFACT != EXECUTION != EVIDENCE != CLAIM
CLAIM_ALLOWED=false
UPSTREAM_INTEGRATION=TOKEN_VAZIO
RELEASE_ALLOWED=false
```
71 changes: 71 additions & 0 deletions contrib/raf_freestanding_gate/include/raf_gate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* RAFAELIA freestanding evidence gate — independent optional experiment.
* Original contribution offered under the InterBase Public License 1.0.
* Provided AS IS, without warranty; not part of Firebird's public ABI.
*
* Warnings are diagnostics, not successes. Do not suppress them to fake a gate.
* Strict types, explicit bounds, and replayable receipts aid inspection.
*/
#ifndef RAF_FREESTANDING_GATE_H
#define RAF_FREESTANDING_GATE_H
#include <stddef.h>
#include <stdint.h>

#define RAF_SHA256_BYTES 32u
#define RAF_RECEIPT_BYTES 114u
#define RAF_RECEIPT_PREFIX_BYTES 82u

typedef enum raf_state {
RAF_BLOCKED = 0,
RAF_PASS = 1,
RAF_ROLLED_BACK = 2,
RAF_FAILBACK = 3,
RAF_UNRECOVERABLE = 4,
RAF_PREPARED = 16
} raf_state;

typedef struct raf_receipt { uint8_t bytes[RAF_RECEIPT_BYTES]; } raf_receipt;
typedef int (*raf_step_fn)(void *context);
/* Return 1 only for atomically durable append, 0 for definite rejection,
* -1 for indeterminate persistence. Never report an unverified success. */
typedef int (*raf_persist_fn)(void *context, const raf_receipt *receipt);

typedef struct raf_ports {
void *context;
raf_step_fn preflight; /* Mandatory, read-only */
raf_step_fn apply; /* Mandatory, reversible and idempotent */
raf_step_fn verify; /* Mandatory, checks postconditions */
raf_step_fn rollback; /* Mandatory, checked compensation */
raf_step_fn failback; /* Optional, switch to independently known-good */
raf_step_fn verify_recovery; /* Mandatory independent recovery postcheck */
raf_persist_fn persist;/* Mandatory, CAS chain/sequence at durable store */
} raf_ports;

typedef struct raf_request {
uint64_t next_seq;
uint8_t previous_sha256[RAF_SHA256_BYTES];
const uint8_t *evidence;
size_t evidence_bytes;
} raf_request;

typedef struct raf_result {
raf_state state;
uint8_t terminal_durable;
raf_receipt prepared;
raf_receipt terminal;
} raf_result;

/* No libc, allocation, I/O, clock, threads, or platform runtime calls. */
void raf_sha256(const uint8_t *data, size_t size, uint8_t digest[RAF_SHA256_BYTES]);
int raf_receipt_create(raf_receipt *out, uint64_t seq, raf_state state,
const uint8_t prev_sha256[RAF_SHA256_BYTES],
const uint8_t *evidence, size_t evidence_bytes);
int raf_receipt_valid(const raf_receipt *r, uint64_t expected_seq,
const uint8_t expected_prev_sha256[RAF_SHA256_BYTES]);
int raf_receipt_evidence_match(const raf_receipt *r, const uint8_t *evidence,
size_t evidence_bytes);
void raf_gate_run(raf_result *out, const raf_request *request, const raf_ports *ports);
/* RFC 5424 header/message text profile only; TLS transport is NOT included. */
int raf_syslog5424_format(char *out, size_t capacity, size_t *written,
const raf_receipt *receipt);
#endif
Loading