# Serial Alice Trust Score Specification v2.0

**Date:** 2026-11-01
**Status:** Public · Matches `trust-score-v2` in production code
**Canonical URL:** https://api.serialalice.pt/docs/trust-score-spec
**Contact:** trust@serialalice.pt

---

## Purpose

The Trust Score is a deterministic number from 0.00 to 1.00 that expresses how
much evidence supports an energy certificate, and from which sources. It is not
a quality rating and it is not chosen by Serial Alice per certificate. It is
computed from the evidence flags in the certificate's `cross_checks`, using the
fixed table below.

Anyone can reproduce a score from those flags.

---

## The Independence Principle

Physical truth requires a source outside the audited host. NVML and RAPL are
both counters on a host the operator controls, so their agreement is internal
consistency, not independent truth. `hardware_attested` therefore needs two
independent pieces of evidence. Host-side evidence alone cannot reach it.

---

## Scoring Table

| Evidence flag               | Points | What it means |
|-----------------------------|--------|---------------|
| `out_of_band_power`         | +0.40  | An independent physical meter (PDU, branch circuit, facility or BMC) corroborates the reported power. A reading relayed by the agent earns this credit only through the relay gate. |
| `tee_attested`              | +0.40  | A TEE quote was verified (Intel TDX and AMD SEV-SNP live in production; AWS Nitro planned). It proves a genuine TEE committed to the samples. It does not identify the code that measured. |
| `signed_exporter`           | +0.30  | The exporter response carried a verified Ed25519 signature. Cryptographic exporter integrity, on the host side. |
| `machine_fingerprint_match` | +0.20  | The exporter's machine identity matches the agent's. |
| `dual_source`               | +0.15  | NVML and RAPL agree. Internal consistency on the host. |
| `tee_mrtd_allowed`          | +0.00  | A qualifier of the same quote, not a second piece of evidence. |

The weights sum to 1.45. The score is clamped to 1.00.

Evidence that was attempted and failed earns no points and is listed in
`evidence_failed`. Evidence that is true but not creditable is listed in
`evidence_withheld`.

Signatures (Ed25519, ML-DSA-65), Merkle proofs and the Polygon anchor protect the
record after signing. They are verified separately and add no points.

---

## Posture Thresholds

| Score                               | Posture             |
|-------------------------------------|---------------------|
| ≥ 0.80                              | `hardware_attested` |
| ≥ 0.40, or `dual_source` true alone | `cross_checked`     |
| otherwise                           | `self_reported`     |

Reference combinations:

| Evidence                                                     | Score | Posture             |
|--------------------------------------------------------------|-------|---------------------|
| `dual_source`                                                | 0.15  | `cross_checked`     |
| `tee_attested` alone                                         | 0.40  | `cross_checked`     |
| `dual_source` + `signed_exporter` + `machine_fingerprint_match` (host-only ceiling) | 0.65 | `cross_checked` |
| `tee_attested` + `out_of_band_power`                         | 0.80  | `hardware_attested` |
| `tee_attested` + `signed_exporter` + `machine_fingerprint_match` | 0.90 | `hardware_attested` |
| `out_of_band_power` + `signed_exporter` + `machine_fingerprint_match` | 0.90 | `hardware_attested` |

---

## What the Score Is Not

- **Not a quality rating.** A high score says nothing about whether the model
  performed well or was efficient.

- **Not chosen by Serial Alice.** The score is computed from the evidence flags
  with the fixed table above.

- **Not a guarantee of accuracy.** A score of 1.00 does not mean the measurement
  is perfectly accurate. Hardware sensors have calibration tolerances.

---

## Computation

```python
EVIDENCE_WEIGHTS = {
    "out_of_band_power":         0.40,
    "tee_attested":              0.40,
    "tee_mrtd_allowed":          0.00,
    "signed_exporter":           0.30,
    "machine_fingerprint_match": 0.20,
    "dual_source":               0.15,
}

def compute_trust_score(cross_checks: dict) -> tuple[float, str]:
    score = min(sum(w for flag, w in EVIDENCE_WEIGHTS.items()
                    if cross_checks.get(flag) is True), 1.0)
    if score >= 0.80:
        posture = "hardware_attested"
    elif score >= 0.40 or cross_checks.get("dual_source") is True:
        posture = "cross_checked"
    else:
        posture = "self_reported"
    return score, posture
```

This omits the withheld-credit rules. The production code is
`app/domain/trust/trust_score.py`.

---

## Certificate Versions

Each certificate carries the version it was scored under in
`trust_model.trust_score.version` and keeps its signed score. Under
`trust-score-v1`, `tee_mrtd_allowed` was worth a further 0.40, so a certificate
with a TEE quote and an allowlisted MRTD reads 0.80 and `hardware_attested`. The
same evidence scores 0.40 and `cross_checked` under `trust-score-v2`. Some older
certificates carry no `trust_score` block.

---

## Verification

```bash
curl https://api.serialalice.pt/v2/certificates/{cert_id}/verify \
  | jq '.trust_posture, .trust_model.trust_score'
```

The top-level `trust_score` field in that response is a separate measurement
confidence value, not the score defined here.

Live ladder: https://api.serialalice.pt/trust-ladder

---

## Changelog

| Version | Date       | Change |
|---------|------------|--------|
| 2.0     | 2026-11-01 | Aligned with `trust-score-v2` in code. Replaced the layer table: NVML, RAPL, ML-DSA-65, Merkle proof and Polygon anchor are not scored. |
| 1.1     | 2026-05-30 | RAPL weight increased from +0.15 to +0.25. Machine Fingerprint folded into Signed Exporter. |
| 1.0     | 2026-05-30 | Initial public release. |

---

*Serial Alice Trust Score Specification v2.0 · Licensed CC BY 4.0*
