# fasten SDK setup · paste this into your coding assistant

Paste this file into Claude Code, Cursor, Codex, Copilot, or any AI coding assistant working on your codebase. It integrates the fasten SDK, wires request_id propagation, and emits a verified first audit row.

## What you'll do

1. Detect the primary language of the target codebase (Python, Go, TypeScript, C++, Swift, JVM).
2. Install the fasten SDK for that language.
3. Register one audit code for the domain you're instrumenting.
4. Add request_id propagation to the primary HTTP transport (or the equivalent shim).
5. Emit one audit row and read it back.
6. Verify the row's hash chain.

## Step 1 · install

fasten SDKs are pre-1.0 · installed from source today, registry publish is planned.

- **Python** (3.10+): clone the repo and `pip install ./python`
- **Go** (1.22+): `go get github.com/nerdapplabs/fasten/go`
- **TypeScript / Node** (24+): clone and `npm install ./js`
- **C++**: pull `nerdapplabs/fasten/cpp`, link against the core lib
- **Swift**: SwiftPM `.package(url: "https://github.com/nerdapplabs/fasten", branch: "main")`
- **JVM**: build locally from `nerdapplabs/fasten/java` (Maven coordinates `sh.fasten:fasten`), install to local Maven

## Step 2 · register a code

Every fasten emit uses a registered code. Codes live in one place per domain.

Python:

```python
from fasten import register, Domain, Meta, Severity, RetentionClass

register(Domain("payments"), {
    "PAYMENT_CAPTURED": Meta(
        summary="Payment captured by processor.",
        severity=Severity.INFO,
        retention_class=RetentionClass.MEDIUM,
    ),
})
```

Go:

```go
fasten.MustRegister(fasten.Domain("payments"), map[fasten.Code]fasten.Meta{
    "PAYMENT_CAPTURED": {
        Summary:        "Payment captured by processor.",
        Severity:       fasten.SevInfo,
        RetentionClass: fasten.RetentionMedium,
    },
})
```

## Step 3 · wire request_id

fasten ships transport shims for the common cases. Add the one matching your primary transport.

Python (FastAPI or Starlette):

```python
from fasten.shim.http import RequestIDMiddleware
app.add_middleware(RequestIDMiddleware)
```

Python (any framework, manually):

```python
from fasten import with_request_id, mint_id
with with_request_id(request.headers.get("X-Request-ID") or mint_id()):
    ...  # every emit inside picks it up ambiently
```

Go (net/http):

```go
handler := fasten.RequestID(myMux)  // stamps ctx and X-Request-ID header
http.ListenAndServe(":8080", handler)
```

Go (manual):

```go
ctx = fasten.WithRequestID(ctx, incomingID)  // or fasten.MintID() if empty
// pass ctx through downstream calls
```

## Step 4 · emit + read

Python:

```python
from fasten import emit
emit(
    code="PAYMENT_CAPTURED",
    target="pay_01H9F2K7ZZ",
    actor="processor-svc",
    actor_kind="service",
    detail={"amount_cents": 1299, "currency": "USD"},
)
```

Go:

```go
fasten.Emit(ctx, "PAYMENT_CAPTURED",
    fasten.Target("pay_01H9F2K7ZZ"),
    fasten.Actor("processor-svc", "service"),
    fasten.WithDetail(map[string]any{"amount_cents": 1299, "currency": "USD"}),
)
```

Read back (either language) by mounting the reader and hitting the API. Python:

```python
from fasten.reader import router as fasten_router
app.include_router(fasten_router(), prefix="/fasten")
```

Then: `curl 'localhost:8080/fasten/logs/audit?target=pay_01H9F2K7ZZ'`

Go:

```go
mux.Handle("/fasten/", http.StripPrefix("/fasten", fasten.NewReader()))
```

Same curl works.

## Step 5 · verify the chain

Prove no row has been rewritten. Python:

```python
from fasten import audit_store, verify_chain
rows = audit_store().query(limit=1000)
rows.reverse()  # verify_chain wants oldest-first
result = verify_chain(rows)
assert result.ok, f"chain broke at {result.first_break_at}: {result.reason}"
```

Go: use the CLI or the `verify_chain` helper on the reader host (see /docs/verify-offline/).

If `result.ok` is `True`, no row in the window has been tampered with since it was written. If not, `first_break_at` is the row where the chain diverges.

## Common gotchas

- **Emit fails with `AuditCatalogError`**: the code isn't registered. Run `register()` (Python) or `MustRegister()` (Go) at startup, once, before any emit.
- **Rows land but reads return empty**: no durable store is configured. Set `FASTEN_AUDIT_DSN=sqlite:///path/audit.db` (Python) or pass `Config.AuditStore` (Go). Without a store, rows live in an in-memory ring only.
- **Every request_id is a sentinel like `orphan-svc-*`**: the HTTP shim isn't wired. Add `RequestIDMiddleware` (Python) or `fasten.RequestID` (Go) around your handler chain.
- **`emit` refuses a non-string detail field where the schema expects a string**: fix the caller; the SDK rejects on shape mismatch to keep the row queryable.

## Environment variables the SDK reads (Python)

- `FASTEN_AUDIT_DSN` · durable audit store (sqlite:// or postgres://)
- `FASTEN_API_DSN` · optional API-log persistence
- `FASTEN_TENANT_ID`, `FASTEN_SERVICE_ID`, `FASTEN_NODE_ID` · identity anchors
- `FASTEN_AUDIT_STORE_FAILURE_STRATEGY` · `queue` (default, async drainer) or `raise` (sync, throws `AuditStoreError`)
- `FASTEN_REDACT_KEYS` · comma-separated extra redaction keys
- `FASTEN_READER_KEY` · auth for the reader endpoints when exposed

Go uses `Config` struct fields; consult `go/fasten.go` for the equivalent names.

## Reading more

- [Docs overview](https://fasten.sh/docs/) · reference
- [Auditor's runbook](https://fasten.sh/docs/verify-offline/) · verify_chain scripts + CI job + break-recovery
- [State over time](https://fasten.sh/docs/state-over-time/) · the temporal anchor
- [Three streams](https://fasten.sh/docs/three-streams/) · audit / Syslog / API access picker
- [How the hash chain proves tamper-evidence](https://fasten.sh/docs/tamper-evidence/) · with worked example
