{"id":51121824,"url":"https://github.com/tegmentum/citadel","last_synced_at":"2026-06-25T03:30:32.605Z","repository":{"id":362546551,"uuid":"1193211353","full_name":"tegmentum/citadel","owner":"tegmentum","description":"A stateful, operator-friendly TPM 2.0 platform: named objects, declarative policies, explainable diagnostics, and a CLI, TUI, and HTTP daemon.","archived":false,"fork":false,"pushed_at":"2026-06-04T20:38:56.000Z","size":391,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T21:18:52.192Z","etag":null,"topics":["attestation","cli","hardware-security","rust","security","tpm","tpm2","trusted-computing","tui"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tegmentum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T01:40:18.000Z","updated_at":"2026-06-04T20:39:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tegmentum/citadel","commit_stats":null,"previous_names":["tegmentum/citadel"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/tegmentum/citadel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fcitadel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fcitadel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fcitadel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fcitadel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tegmentum","download_url":"https://codeload.github.com/tegmentum/citadel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fcitadel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34758773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attestation","cli","hardware-security","rust","security","tpm","tpm2","trusted-computing","tui"],"created_at":"2026-06-25T03:30:31.801Z","updated_at":"2026-06-25T03:30:32.598Z","avatar_url":"https://github.com/tegmentum.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Citadel\n\nCitadel is a **TPM-backed distributed trust platform**: it roots trust in each node's hardware TPM, agrees on that trust across a mesh, and uses it to gate secrets, workload identity, and containment — with a full observability plane over the whole fabric. Identity and access become *continuously earned* properties of a node, not one-time admissions.\n\nAt its base is **`citadel tpm`**, an operator-friendly TPM toolkit (a resource-oriented replacement for low-level `tpm2-tools`). On top of it Citadel runs an attestation mesh, a verifying control plane, Mesh-Sealed Secrets, SPIFFE/SPIRE identity, Prometheus/OpenTelemetry observability, and a set of mesh primitives (a freshness beacon, continuously-earned capabilities, a witnessed fact ledger, a threshold CA, tripwires, and cross-mesh federation). It supports TPM 2.0 and, as a capability-gated tier, TPM 1.2.\n\n## The platform\n\n| Layer | What it does | Reference |\n|---|---|---|\n| **TPM toolkit** — `citadel tpm` | Named objects, declarative policies, measured boot, sealing, remote attestation, a tamper-evident audit log, and the Measured Merkle Anchor. | this README; `tpm-core`, `tpm-tui` |\n| **Attestation mesh** — `citadel-mesh` | SWIM gossip + HRW witness assignment; nodes attest each other and reach **witness-quorum trust** (categorical, signed verdicts), with gossip-wired quarantine + Reed-Solomon evidence on compromise. | [`distributed-attestation-mesh.md`](docs/design/distributed-attestation-mesh.md) |\n| **Control plane** — `citadel-control-plane` | A **verifying aggregator** (explicitly *not* a root of trust): re-verifies every verdict, derives trust, serves an agreement-first dashboard + API over pluggable durable storage (Mem/redb/Postgres) with HRW sharding. Load-verified at **10k nodes (~19k verdicts/s)**. | [`control-plane-roadmap.md`](docs/design/control-plane-roadmap.md) |\n| **Mesh-Sealed Secrets** — `citadel-mss` | Quorum-gated, lease-bound, TPM-enforced secret release; threshold custody (Shamir) and threshold signing (FROST + DKG) over mesh gossip; **churn-resilient dynamic committees** — proactive resharing with generation fencing, so a failed holder is replaced as identities rotate (constant cluster size). | [`mss-roadmap.md`](docs/design/mss-roadmap.md) |\n| **Workload identity** — `citadel-spiffe`, `citadel-spire-*`, `citadel-trust-sync` | Mesh trust gates SPIRE SVID issuance: a NodeAttestor plugin (with AutoMTLS + the agent pair), a registration controller, and a continuous trust synchronizer that revokes on quarantine. | [`spiffe-roadmap.md`](docs/design/spiffe-roadmap.md) |\n| **Observability** — `citadel-otel-schema`, `-metrics-exporter`, `-telemetry` | Security-state Prometheus `/metrics`, OpenTelemetry logs + the containment trace, plus tool-validated alert rules / Grafana dashboards / Collector config and multi-cluster federation. | [`observability-roadmap.md`](docs/design/observability-roadmap.md) |\n| **Mesh primitives** — `citadel-{beacon,caps,facts,ca,tripwire,federation}` | Six primitives riding the witnessed-quorum core: a **freshness/randomness beacon** (threshold-signed, with an optional unbiasable BLS scheme), **continuously-earned capabilities**, a **witnessed fact ledger** (the mesh as a notary), a **threshold CA** (a key no node holds), **tripwires/honeytokens** → quarantine, and **cross-mesh federation**. | [`mesh-primitives-roadmap.md`](docs/design/mesh-primitives-roadmap.md) |\n\nThe mesh and control plane are queryable from the CLI via **`citadel cluster`** (below). The rest of this README documents the `citadel tpm` toolkit; see the per-layer design docs above for the trust fabric.\n\n## Why the TPM toolkit\n\nThe existing TPM toolchain is powerful but hostile. Common workflows require explicit context file management, manual command sequencing, opaque error codes, and deep spec knowledge. `citadel tpm` makes the TPM feel like a managed object store:\n\n- **Named objects** instead of context files and raw handles\n- **Workflow collapsing** — `citadel tpm key create` does the right create/load/persist sequence\n- **Explainable diagnostics** — cause chains, context, actionable next steps\n- **Structured output** — `--json` or `--format yaml` on every command\n- **Declarative policies** — YAML policy files compiled and tested before use\n- **Auto-detection** — finds hardware TPM, vTPM, or falls back to mock\n\n## Install\n\n```bash\nbash install.sh\n```\n\nThis builds a release binary with vTPM support, installs it to `~/.local/bin/citadel`, installs the libtpms WASM component to `~/.local/share/tpm/` (from a local libtpms-wasm build if present, otherwise downloaded from the [libtpms-wasm](https://github.com/tegmentum/libtpms-wasm) release), and sets up shell completions.\n\n### From source (manual)\n\n```bash\ncargo build --release --features vtpm\ncp target/release/citadel ~/.local/bin/\n```\n\n### Shell completions\n\n```bash\ncitadel tpm completions zsh  \u003e ~/.zsh/completions/_citadel\ncitadel tpm completions bash \u003e ~/.local/share/bash-completion/completions/citadel\ncitadel tpm completions fish \u003e ~/.config/fish/completions/citadel.fish\n```\n\n## Quick start\n\n```bash\ncitadel tpm init\ncitadel tpm key create signing/release\ncitadel tpm key sign signing/release --input artifact.tar.gz\ncitadel tpm key list\ncitadel tpm --json status\ncitadel                             # launches TUI\n```\n\n## Backends\n\nThe `--backend` flag (or `TPM_BACKEND` env var) selects the TPM backend. Default is `auto`.\n\n| Backend | Description |\n|---------|-------------|\n| `auto` | Probe in order: hardware TPM, vTPM, mock (default) |\n| `device` | Hardware TPM at `/dev/tpmrm0` (requires `--features tpm-hw`) |\n| `vtpm` | In-process libtpms via WASM/wasmtime (requires `--features vtpm`) |\n| `mock` | Deterministic in-memory mock for development |\n| `tpm12` | Software-modeled **TPM 1.2** tier (RSA-only, SHA-1 PCRs, no policy sessions) — exercises the 1.2 device class |\n\nAuto-detection checks for `/dev/tpmrm0` first, then looks for the vTPM component at `~/.local/share/tpm/tpm-ephemeral.component.wasm`, and falls back to mock if neither is found.\n\nThe vTPM persists its state alongside the metadata store at `\u003cstore\u003e.tpmstate`, so keys, NV, and saved PCRs (0–15) survive across separate `citadel tpm` invocations — signing, sealing, attestation, and seal-to-attested-set work end-to-end on the virtual TPM, not just the mock. Permanent state restores on startup; volatile state (PCRs/sessions) is checkpointed with `Shutdown(STATE)` and resumed with `Startup(STATE)`. Note that PCRs 16–23 are resettable and not in the save set, so use a PCR in 0–15 when you need a measurement anchor to persist across invocations.\n\n## Command groups\n\n`citadel` is the umbrella CLI:\n\n- **`citadel tpm \u003c…\u003e`** — the TPM operator platform (everything documented below).\n- **`citadel cluster \u003c…\u003e`** — operator queries against the Citadel control plane\n  (the mesh trust fabric): `status` (mesh health), `nodes`, and `metrics`\n  (Prometheus exposition). Point it at the control plane with `--endpoint` or\n  `CITADEL_CONTROL_PLANE`.\n\n```bash\ncitadel cluster status   --endpoint http://control-plane:8080\ncitadel cluster nodes    --endpoint http://control-plane:8080\ncitadel cluster metrics  --endpoint http://control-plane:8080\n```\n\n## Commands\n\n### Workspace\n\n```bash\ncitadel tpm init [--profile \u003cname\u003e]        # initialize workspace\ncitadel tpm status                         # backend + workspace + health score\ncitadel tpm doctor                         # diagnostic health checks\ncitadel tpm capabilities                   # algorithms, PCR banks, limits\ncitadel tpm debug --output bundle.json     # collect diagnostic bundle\ncitadel tpm workspace info                 # workspace summary\ncitadel tpm workspace export --output f    # export metadata to JSON\ncitadel tpm workspace import --input f     # import metadata from JSON\n```\n\n### Keys\n\n```bash\ncitadel tpm key create signing/release [--algorithm ecc-p256] [--policy boot-policy]\ncitadel tpm key list [--json]\ncitadel tpm key show signing/release\ncitadel tpm key sign signing/release --input file.bin [--output sig.bin]\ncitadel tpm key delete signing/release\ncitadel tpm key export-pub signing/release [--export-for openssl|ssh|cosign|pkcs11]\ncitadel tpm key rotate signing/release\n```\n\n### Secrets\n\n```bash\ncitadel tpm secret seal db/password --input secret.txt [--policy boot-seal]\ncitadel tpm secret unseal db/password [--output recovered.txt]\ncitadel tpm secret list\n```\n\n### Remote attestation\n\n```bash\ncitadel tpm attest ak-create attest/main\ncitadel tpm attest quote --ak attest/main --pcr 0,7,11 --nonce challenge --output quote.json\ncitadel tpm attest verify --quote quote.json --nonce challenge\n```\n\n### NV storage\n\n```bash\ncitadel tpm nv define config/build-id --size 64\ncitadel tpm nv write config/build-id --input data.txt\ncitadel tpm nv read config/build-id [--output data.txt]\ncitadel tpm nv list\ncitadel tpm nv delete config/build-id\n```\n\n### PCR operations\n\n```bash\ncitadel tpm pcr show --bank sha256 --index 0,7,11\ncitadel tpm pcr baseline save clean-boot --index 0,7,11\ncitadel tpm pcr baseline diff clean-boot\ncitadel tpm pcr baseline list\n```\n\n### Policies\n\n```bash\ncitadel tpm policy create boot-policy --pcr 7,11 [--password]\ncitadel tpm policy compile policy.yaml\ncitadel tpm policy test boot-policy\ncitadel tpm policy explain boot-policy\ncitadel tpm policy show boot-policy\ncitadel tpm policy list\ncitadel tpm policy delete boot-policy\ncitadel tpm policy approve --authority release --pcr 14   # approve the live measured state for PolicyAuthorize-bound keys\n```\n\nPolicy YAML format:\n\n```yaml\nname: boot-integrity\nrequires:\n  pcr:\n    - index: 7\n    - index: 11\n  auth_value: true\n```\n\n### Objects\n\n```bash\ncitadel tpm object list\ncitadel tpm object tree\ncitadel tpm object dependents signing/key\ncitadel tpm object rename old/name new/name\ncitadel tpm object retire signing/old\ncitadel tpm object activate signing/old\n```\n\n### Profiles\n\nProfiles are mutable defaults applied to new operations. They can include constraints that enforce algorithm restrictions, path prefixes, and approval requirements.\n\n```bash\ncitadel tpm profile list\ncitadel tpm profile show [name]\ncitadel tpm profile set ci-signer\n```\n\n### Identities\n\nAn identity bundles a backing key, an intended usage, an optional policy, and certificate metadata into one named object. Audit checkpoint signing references identities by name.\n\n```bash\ncitadel tpm identity init auditor [--usage code-signing] [--algorithm ecc-p256] [--policy boot-policy]\ncitadel tpm identity show auditor\ncitadel tpm identity list\ncitadel tpm identity rotate auditor\ncitadel tpm identity delete auditor\n```\n\nA signing key can be bound to a measured state so the TPM itself refuses to sign unless the host matches it:\n\n```bash\ncitadel tpm identity init anchor --pcr-bind 14                       # frozen: signs only while PCR 14 matches creation time\ncitadel tpm identity init release --authority                        # an offline PolicyAuthorize approver (the upgrade root)\ncitadel tpm identity init anchor --authorized-by release --pcr-bind 14   # upgradable: signs any state `release` has approved\n```\n\n`--pcr-bind` freezes the key to one PCR state (an upgrade would brick it). `--authorized-by` binds it to an authority key instead (TPM2_PolicyAuthorize): the same key keeps signing across upgrades, gated on a witnessed approval — see [Measured Merkle Anchor](#measured-merkle-anchor-mma) below.\n\n### Secure audit log\n\n`citadel tpm audit` is a tamper-evident secure log: entries are hash-chained, sealed into Merkle-rooted segments, and checkpoint-signed by a TPM-backed identity. An anti-rollback head file guards against truncation, payloads can be envelope-encrypted under a master KEK, and segment heads can be co-signed by an external witness. Multiple independent streams (each with a confidentiality tier) are supported.\n\n```bash\ncitadel tpm audit append --event user.login --payload \"session opened\"   # append a hash-chained entry\ncitadel tpm audit head                                  # highest seqno for a stream\ncitadel tpm audit show 1                                # read an entry by seqno\ncitadel tpm audit chain verify                          # verify the hash chain\ncitadel tpm audit segments close                        # seal the open window into a Merkle segment\ncitadel tpm audit sign --identity auditor 1             # TPM-sign a closed segment's checkpoint\ncitadel tpm audit verify                                # verify the checkpoint chain from genesis to head\ncitadel tpm audit prove 1                               # build a Merkle inclusion proof for an entry\ncitadel tpm audit rollback                              # check the anti-rollback head file vs the database\ncitadel tpm audit publish                               # emit witness-submission JSON for the current head\ncitadel tpm audit witness list                          # manage witness receipts\ncitadel tpm audit streams list                          # multi-stream management\ncitadel tpm audit key ...                               # master KEK for envelope-encrypted payloads\n```\n\nThe implementation lives in the standalone `secure-log` workspace (a sibling repo, reused across projects) and is re-exported as `tpm_core::secure_log`; `tpm audit` and the `tpmd` witness endpoint persist to a `secure-log-sqlite` store alongside the metadata database.\n\n### Measured Merkle Anchor (MMA)\n\nMMA extends the TPM's chain of trust past the kernel: applications and artifacts are measured, the measurements branch into a Merkle tree, and the root is checkpoint-signed by a TPM-backed identity and anchored into a PCR. Citadel can also measure *itself* (`measure enroll`), so a quote attests which agent produced the anchor.\n\n```bash\ncitadel tpm measure file /usr/bin/app --kind binary       # measure an artifact into the MMA stream\ncitadel tpm measure enroll --pcr 14 [--verify-ima]         # self-enroll Citadel's own binary (IMA-corroborated)\ncitadel tpm measure checkpoint [--extend-pcr 14]           # seal pending measurements into a Merkle segment, anchor the root\ncitadel tpm measure sign --identity anchor 1               # TPM-sign the segment's root (the anchoring key)\ncitadel tpm measure verify 1                               # verify a measurement is included under a signed root\ncitadel tpm measure rollback-check                         # detect anchor-counter truncation\n```\n\n#### Upgrading without re-keying (the upgrade ceremony)\n\nA measurement is blind to intent: a legitimate upgrade and a tamper are the same observable event — a previously-unseen measurement. Only an **authorization** distinguishes them. So an MMA signing key can be bound to an offline **authority** (TPM2_PolicyAuthorize) rather than to one frozen PCR state: it signs under *any* state the authority approves, and an upgrade is a new approval — not a key ceremony. Approvals land in the witnessed MMA log, so every authorized state change is public and attributable; an unapproved state is exactly the one with **no witnessed approval**, and the key refuses to sign it.\n\n```bash\n# One-time setup: an offline approver, and a signing key bound to it.\ncitadel tpm identity init release --authority                          # hold this key offline (HSM / air-gapped / quorum)\ncitadel tpm identity init anchor --authorized-by release --pcr-bind 14\n\n# Each release: build, deploy, then authorize the new measured state.\ncitadel tpm measure enroll --pcr 14 --verify-ima                       # the new binary's measurement enters the MMA\ncitadel tpm policy approve --authority release --pcr 14                # authority signs the new state → witnessed in the log\ncitadel tpm measure checkpoint --extend-pcr 14\ncitadel tpm measure sign --identity anchor 1                           # the SAME key signs — no re-key across the upgrade\n```\n\nThe TPM signing key never changes across upgrades; the MMA log, anti-rollback counter, and checkpoint chain stay continuous, and `citadel tpm attest verify` surfaces the approving authority and whether the signing state was logged-approved. Because a compromised authority makes attacks indistinguishable from upgrades, keep it offline and prefer M-of-N quorum with reproducible builds. See [`docs/design/mma-upgrade.md`](docs/design/mma-upgrade.md) for the full threat model.\n\n### Maintenance\n\n```bash\ncitadel tpm repair scan\ncitadel tpm repair plan\ncitadel tpm repair apply\ncitadel tpm gc plan\ncitadel tpm gc apply\ncitadel tpm log show [--object path] [--action filter] [--limit 50]\n```\n\n### Recovery\n\n```bash\ncitadel tpm recover list\ncitadel tpm recover show tpm-cleared\n```\n\nPlaybooks: `tpm-cleared`, `handle-mismatch`, `profile-drift`, `boot-change`, `metadata-corruption`, `key-rotation`.\n\n### Education\n\n```bash\ncitadel tpm explain pcr\ncitadel tpm template list\ncitadel tpm template show ci-signer\n```\n\nTopics: `pcr`, `policy`, `hierarchy`, `key`, `seal`, `attestation`, `nv`, `ek`, `ak`, `handle`, `session`, `dictionary-attack`.\n\n## Global flags\n\n| Flag | Description |\n|------|-------------|\n| `--json` | Output as JSON (shorthand for `--format json`) |\n| `--format text\\|json\\|yaml` | Output format |\n| `--backend auto\\|mock\\|tpm12\\|device\\|vtpm` | TPM backend |\n| `--store-path \u003cpath\u003e` | Metadata store location |\n| `--plan` | Dry-run mode — show what would happen |\n| `--verbose` | Debug logging |\n\nEnvironment variables: `TPM_STORE_PATH`, `TPM_BACKEND`, `TPM_VTPM_COMPONENT`.\n\n## TUI\n\nRunning `citadel` with no arguments in a terminal launches the interactive TUI.\n\n| Key | Action |\n|-----|--------|\n| `1`-`4` | Switch view (dashboard, objects, policies, audit) |\n| `Tab` | Cycle views |\n| `j`/`k` | Navigate |\n| `Enter` | Detail view |\n| `n` | Create new key |\n| `d` | Delete selected object |\n| `r` | Refresh |\n| `q`/`Esc` | Back/quit |\n\nThe dashboard shows health score, backend status, object counts, and the active profile.\n\n## Daemon\n\n`tpmd` is an HTTP daemon that exposes TPM operations as a REST API with authentication, approval workflows, and audit logging.\n\n```bash\ntpmd                              # start on 127.0.0.1:7701 (mock backend)\nTPMD_LISTEN=0.0.0.0:8443 tpmd    # custom address\nTPMD_API_KEY=secret tpmd          # require X-API-Key header\nTPMD_TLS_CERT=cert.pem TPMD_TLS_KEY=key.pem tpmd  # TLS with an on-disk key\n```\n\nBy default `tpmd` runs the software (mock) backend. Build with `--features\nvtpm` and set `TPMD_BACKEND=vtpm` (+ `TPM_VTPM_COMPONENT`) to run on the real\nlibtpms-WASM vTPM — required for real signing, including the TPM-held TLS key\nbelow. vTPM state persists at `TPMD_VTPM_STATE` (default `\u003cstore\u003e.tpmstate`).\n\n```bash\nTPMD_BACKEND=vtpm TPM_VTPM_COMPONENT=/path/to/tpm.component.wasm tpmd\n```\n\n#### TLS with a TPM-held server key\n\nThe daemon can terminate TLS using a TPM-resident key instead of a private\nkey file — the key never exists on disk, and the TLS handshake is signed\ninside the TPM. Point it at a citadel **identity** whose key is TPM-backed:\n\n```bash\nTPMD_TLS_IDENTITY=tpmd-tls tpmd        # server key = identity 'tpmd-tls', signed in the TPM\n```\n\nThe certificate is taken from the identity's stored `certificate_pem`, or\nfrom `TPMD_TLS_CERT` if set; its public key must match the identity's TPM\nkey. `TPMD_TLS_IDENTITY` takes precedence over the on-disk `TPMD_TLS_KEY`\npath. This path requires an ECDSA-capable backend (the vTPM or hardware TPM)\nsince the handshake is signed by the TPM; the software mock cannot terminate\na live TLS handshake.\n\n### API\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| GET | /v1/status | Backend info |\n| GET | /v1/health | Health posture and score |\n| GET | /v1/keys | List keys |\n| POST | /v1/keys | Create key `{\"path\":\"...\"}` |\n| GET | /v1/keys/:path | Key details |\n| POST | /v1/sign/:path | Sign data `{\"data_hex\":\"...\"}` |\n| POST | /v1/delete/:path | Delete object |\n| GET | /v1/objects | List all objects |\n| GET | /v1/policies | List policies |\n| POST | /v1/policies | Create policy |\n| GET | /v1/secrets | List sealed secrets |\n| GET | /v1/audit | Audit log |\n| POST | /v1/audit/witness | Submit a secure-log witness co-signature |\n| GET | /v1/approvals | List approval requests |\n| POST | /v1/approvals | Request approval |\n| POST | /v1/approvals/:id/approve | Approve |\n| POST | /v1/approvals/:id/deny | Deny |\n\nApprovals are persisted to the SQLite store and survive daemon restarts.\n\n## Diagnostics\n\nErrors use stable codes and follow a Rust-compiler-inspired format:\n\n```\nerror[TPM0004]: object not found: signing/missing\n\n  causes:\n    - no object with path 'signing/missing' exists in the workspace\n\n  path  signing/missing\n\n  next steps:\n    1. run `citadel tpm object list` to see all objects\n    2. run `citadel tpm key list` to see available keys\n```\n\n27 diagnostic codes covering transport, objects, store, backend, policy, NV, attestation, repair, and internal errors.\n\n## Architecture\n\n```\ncitadel tpm (CLI + TUI)          tpmd (HTTP daemon)       tpm-wasi (WASM CLI)\n       |                        |                        |\n       v                        v                        v\n                    tpm-core (shared library)\n                         |\n            +------------+------------+\n            |            |            |\n         Store       Backend     Diagnostics\n      (trait-based)   (trait)    (27 codes)\n            |            |\n       +---------+  +----+----+----+\n       |         |  |    |    |    |\n    SQLite   Memory Mock  HW   vtpm\n   (native)  (WASM)     (esapi)   (libtpms)\n```\n\nThe store is abstracted behind a `StoreBackend` trait. Native builds use SQLite; WASM builds use an in-memory backend. `tpm-core` compiles to `wasm32-wasip2`:\n\n```bash\ncargo build -p tpm-core --target wasm32-wasip2 --no-default-features\n```\n\nThe tamper-evident secure log lives outside this tree in the standalone `secure-log` workspace (sibling repo), so it can be reused by other projects. `tpm-core` path-depends on its `secure-log` / `secure-log-sqlite` crates and re-exports them as `tpm_core::secure_log`; a `TpmCheckpointSigner` adapts the TPM backend and identity store to the crate's `CheckpointSigner` trait for checkpoint signing.\n\n## Development\n\n```bash\ncargo build --workspace                    # build all\ncargo test --workspace                     # the full workspace suite (mesh, control plane, MSS, SPIFFE, observability, tpm)\ncargo build --features vtpm                # with vTPM backend\ncargo build --features tpm-hw              # with hardware TPM\n\n# Run vTPM smoke tests against real libtpms\nTPM_VTPM_COMPONENT=path/to/tpm-ephemeral.component.wasm \\\n  cargo test --features vtpm --test vtpm_smoke\n\n# Build WASI CLI\ncargo build -p tpm-wasi --target wasm32-wasip2\nwasmtime run target/wasm32-wasip2/debug/tpm-wasi.wasm status\n```\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftegmentum%2Fcitadel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftegmentum%2Fcitadel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftegmentum%2Fcitadel/lists"}