No description
  • Rust 67.7%
  • Svelte 18.5%
  • TypeScript 10.3%
  • CSS 3%
  • GDB 0.3%
  • Other 0.2%
Find a file
Tan Siewert f371c7c0d6
Some checks failed
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / build-web (push) Has been cancelled
CI / docker (push) Has been cancelled
Real initial commit
Signed-off-by: Tan Siewert <tan@siewert.io>
2026-06-30 14:25:54 +02:00
.config Add placeholder test for CI to pass 2026-05-11 17:19:21 +02:00
.forgejo/workflows Real initial commit 2026-06-30 14:25:54 +02:00
config Real initial commit 2026-06-30 14:25:54 +02:00
examples Real initial commit 2026-06-30 14:25:54 +02:00
ffdc-api Real initial commit 2026-06-30 14:25:54 +02:00
ffdc-core Real initial commit 2026-06-30 14:25:54 +02:00
ffdc-tui Real initial commit 2026-06-30 14:25:54 +02:00
web-ui Real initial commit 2026-06-30 14:25:54 +02:00
.dockerignore Real initial commit 2026-06-30 14:25:54 +02:00
.gitattributes Real initial commit 2026-06-30 14:25:54 +02:00
.gitignore Real initial commit 2026-06-30 14:25:54 +02:00
Cargo.lock Real initial commit 2026-06-30 14:25:54 +02:00
Cargo.toml Real initial commit 2026-06-30 14:25:54 +02:00
compose.gdb.yml Real initial commit 2026-06-30 14:25:54 +02:00
compose.yml Real initial commit 2026-06-30 14:25:54 +02:00
Dockerfile Real initial commit 2026-06-30 14:25:54 +02:00
Dockerfile.gdb Real initial commit 2026-06-30 14:25:54 +02:00
README.md Real initial commit 2026-06-30 14:25:54 +02:00

FFDC Parser

Parses OpenBMC First Failure Data Capture (FFDC) dump archives. Extracts, classifies, and renders structured data from obmcdump tarballs.

Architecture

modular crate:

Crate Description
ffdc-core Core library: compression detection, archive extraction, file classification, structured parsers, journal parser, coredump analysis, SQLite metadata store
ffdc-api Axum REST API with upload, job tracking, search, compare, OIDC session auth
ffdc-tui Terminal UI (ratatui) with Browse, View, and Search modes
web-ui SvelteKit static SPA frontend

Usage (API)

ffdc-api --db-path /path/to/ffdc.db
Flag / Env Default Description
--bind / FFDC_BIND 0.0.0.0:3000 Listen address
--db-path / FFDC_DB_PATH ffdc.db SQLite database path
--cache-dir / FFDC_CACHE_DIR cache Upload extraction cache
--max-upload-bytes / FFDC_MAX_UPLOAD_BYTES 8388608 Max upload size
--auth-enabled / FFDC_AUTH_ENABLED false Require authentication
--session-ttl-secs / FFDC_SESSION_TTL_SECS 86400 Session cookie TTL
--oidc-issuer / FFDC_OIDC_ISSUER - OIDC issuer URL
--oidc-client-id / FFDC_OIDC_CLIENT_ID - OIDC client ID
--oidc-client-secret / FFDC_OIDC_CLIENT_SECRET - OIDC client secret
--oidc-redirect-url / FFDC_OIDC_REDIRECT_URL - OIDC callback URL
--static-dir / FFDC_STATIC_DIR - SPA static files dir

Usage (TUI)

ffdc-tui /path/to/dumps/

Browse, inspect, and search FFDC dump archives interactively:

  • Browse mode - list dumps in the left panel, select files in the right panel, view parsed content or raw hex in the main area
  • View mode - scroll through file content with j/k, search with / when browsing a file
  • Search mode - press / to enter a regex query, n/N to cycle hits, results highlighted across all dumps and files
  • Save a file with s, quit with q or Esc
Key Mode Action
Tab Browse Focus toggle: dump list / file list / content
/ / j/k Any Move selection or scroll content
/ l Browse Enter selected dump to expand
/ h Browse Collapse or go up
Enter Browse View selected file
/ Browse Start search
n / N Search Next / previous match
Esc Any Cancel search or go back
s Browse Save current file to disk
q Any Quit

Web UI

cd web-ui && npm install && npm run build

Serve the built files from web-ui/build/ - either set FFDC_STATIC_DIR on the API or serve with any HTTP server.

E2E Tests

Uses Playwright against the real Rust API server with the built SvelteKit UI.

cd web-ui
npm install
npx playwright install chromium   # one-time: download browser binaries
npm run build
npm run test:e2e

The playwright.config.ts automatically builds the UI, starts the Rust API server on port 3000 (serving both the API and the static UI), runs tests, and shuts down.

To run against an externally managed API (e.g. already running on port 3000 with the UI on a SvelteKit dev server at port 5173):

PLAYWRIGHT_BASE_URL=http://localhost:5173 npx playwright test

This skips the built-in webServer and points the tests at your UI origin. The API is expected to be already running (at any port your UI proxies to).

Authentication

When --auth-enabled is set, the API uses OIDC (Authorization Code + PKCE) with httpOnly session cookies.

Flow:

  1. User clicks "Sign in with SSO" on the login page
  2. Browser is redirected to /api/v1/auth/oidc/start, which generates a PKCE challenge and redirects to the OIDC provider
  3. Provider authenticates the user and redirects back to /api/v1/auth/oidc/callback
  4. Server exchanges the authorization code (with PKCE verifier), fetches userinfo, creates a session, sets an httpOnly cookie, and redirects to /
  5. All subsequent requests include the session cookie automatically

When --auth-enabled is false (default), all routes are accessible without authentication.

Docker

docker compose up --build

Runs the API on port 3000 with persistent storage. Set FFDC_AUTH_ENABLED=true and OIDC environment variables in compose.yml for auth.

GDB coredump analysis

The gdb Cargo feature adds automated coredump analysis: backtrace, crashing thread, signal, and register state. Analysis requires a Yocto cross-GDB binary and the matching target sysroot for debug-symbol resolution.

Because BMC hardware families use different CPU architectures (ARM 32-bit, AArch64, RISC-V, …), each with its own SDK, the server supports multiple SDKs simultaneously. The correct SDK is selected automatically based on the archive filename.

How SDK selection works

After the archive is parsed, the server reads the dump's own metadata to pick the right SDK, not the archive filename (which is unreliable)!

Dump field Source file Example value
hostnamectl.architecture hostnamectl output "arm", "arm64"
hostnamectl.hostname hostnamectl output "rainier-bmc"
os-release.TARGET_MACHINE os-release "ast2600"

Selection rules:

  1. Each SDK entry is checked in order. Entries with at least one non-empty criterion are checked first (specific match).
  2. architectures must match hostnamectl.architecture (substring, case-insensitive).
  3. machines must match TARGET_MACHINE or hostname (substring, case-insensitive).
  4. Both criteria are ANDed. An empty list means "any" for that criterion.
  5. If no specific entry matches, the first catch-all entry (both lists empty) is used.
  6. If still no match, the on_no_match policy applies: "skip" (default), "warn", "error".

If automatic selection picks the wrong SDK (e.g. because metadata is missing), the user can override it explicitly. See Manual SDK override below.

1. Create an SDK config file

Copy the example and fill in the real paths for your SDKs:

cp config/sdks.example.json config/sdks.json
{
  "sdks": [
    {
      "name": "ARM Cortex-A (AST2600 / AST2500)",
      "gdb_path": "/sdks/arm/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gdb",
      "sysroot": "/sdks/arm/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi",
      "architectures": ["arm"],
      "machines": ["ast2600", "ast2500", "rainier", "everest"]
    },
    {
      "name": "AArch64 (AST2700)",
      "gdb_path": "/sdks/aarch64/sysroots/x86_64-pokysdk-linux/usr/bin/aarch64-poky-linux/aarch64-poky-linux-gdb",
      "sysroot": "/sdks/aarch64/sysroots/cortexa35-poky-linux",
      "architectures": ["arm64", "aarch64"],
      "machines": ["ast2700", "bletchley"]
    }
  ],
  "on_no_match": "skip"
}

Fields:

Field Required Description
name yes Unique label; used in logs and for manual override
gdb_path yes Absolute path to the cross-GDB binary inside the SDK
sysroot no Absolute path to the target sysroot (enables debug-symbol loading)
architectures no hostnamectl.architecture substrings; empty means any arch
machines no TARGET_MACHINE or hostname substrings; empty means any machine

on_no_match values: "skip" (default) · "warn" · "error"

Manual SDK override

If no SDK matched (GDB analysis was skipped) or the wrong SDK was selected, trigger a re-parse with an explicit SDK name:

# 1. List available SDKs
curl http://localhost:3000/api/v1/sdks

# 2. Re-parse with a specific SDK
curl -X POST http://localhost:3000/api/v1/ffdc/<uuid>/reparse \
     -H 'Content-Type: application/json' \
     -d '{"sdk": "ARM Cortex-A (AST2600 / AST2500)"}'

The sdk field must match the name of an entry in your SDK config exactly. Omit the body (or omit the sdk field) to use automatic selection.

2. Build and run

Place your Yocto SDK installer scripts in the repo root, then use the compose.gdb.yml overlay which switches the build to Dockerfile.gdb:

cp config/sdks.example.json config/sdks.json   # fill in your paths
docker compose -f compose.yml -f compose.gdb.yml up --build

compose.gdb.yml sets FFDC_SDK_CONFIG=/sdks/sdks.json. Bake your sdks.json into the image by placing it in the repo root and adding a COPY sdks.json /sdks/sdks.json line to Dockerfile.gdb, or mount it at runtime:

docker run -p 3000:3000 \
  -v /path/to/data:/data \
  -v $(pwd)/config/sdks.json:/sdks/sdks.json:ro \
  -e FFDC_SDK_CONFIG=/sdks/sdks.json \
  ffdc-api-gdb

See the comments inside Dockerfile.gdb for SDK installer filenames and install-path constraints.

Single-target setup

If you only have one architecture, create a sdks.json with a single entry and leave both architectures and machines empty so it acts as a catch-all:

{
  "sdks": [
    {
      "name": "ARM Cortex-A",
      "gdb_path": "/sdks/arm/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gdb",
      "sysroot": "/sdks/arm/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi",
      "architectures": [],
      "machines": []
    }
  ],
  "on_no_match": "skip"
}

Development

Build

Each crate builds independently:

cargo build -p ffdc-core
cargo build -p ffdc-api
cargo build --workspace

Test & Lint

cargo test
cargo clippy --workspace
cargo doc --no-deps --document-private-items