What provenance signatures must accompany a synthesized multi-agent report to prevent authority laundering?
**Date of Record:** 2026 October 27 **Scope:** Applies when multiple agents with different privilege levels contribute to a single synthesized report (e.g., a research summary, an incident report, or a compliance document), and the report must preserve the privilege level of each contribution, preventing low-privilege content from being attributed to a high-privilege agent. Does not apply to reports where all contributions come from agents with identical privilege levels.
Core Distinction
The separation between **report authorship** (the agent that compiled the final document) and **contribution provenance** (the original agent behind each piece of content). Relying on report authorship creates the **Authority Laundering Attack**: a low-privilege agent contributes a claim, a high-privilege agent compiles it into a report, and downstream consumers assume the claim carries the high-privilege agent's authority, bypassing access controls and trust boundaries.
What Was Observed
In multi-agent research and reporting architectures, failing to preserve contribution provenance produces three primary failure modes:
- **Privilege Bypass:** A restricted-data agent contributes a claim to a report, and the report is published under the high-privilege agent's signature, exposing the restricted data.
- **Attribution Erosion:** A malicious agent injects a false claim into a report by contributing it to a compiling agent. The final report attributes the false claim to the compiler, not the malicious agent.
- **Compliance Violation:** The report mixes content of different classifications (e.g., PII, public, confidential) without provenance, so compliance audits cannot verify data handling.
Recorded Elements
- **`per_contribution_signature`:** For each contribution, the cryptographic signature of the contributing agent's identity and privilege level.
- **`contribution_privilege_tier_map`:** The mapping of each contribution to its privilege tier (e.g., `PUBLIC`, `INTERNAL`, `CONFIDENTIAL`), ensuring no laundering across tiers.
- **`compiler_signature`:** The compiler's cryptographic signature over the entire report, attesting to the report's structure but not claiming authority over each contribution.
- **`contribution_attachment_manifest`:** The complete list of contributions with their original signatures, packaged as an appendix to the report, so consumers can verify provenance even if the report body aggregates the content.
Condition of Validity
* **Applies when:** Contributors have different privilege levels, the report is consumed by downstream agents or humans who rely on privilege attribution, and the report aggregates content in a way that obscures individual contributions. * **Does not apply when:** All contributors have identical privilege levels (no laundering risk), or the report contains only contributions from a single agent.
Decision Boundary
- **Did multiple agents with different privilege levels contribute?** If no, standard report signing applies. If yes, proceed.
- **Is a `per_contribution_signature` present for every contribution?** If no, some contributions cannot be attributed to their original agent. The report must be rejected.
- **Does the `contribution_privilege_tier_map` show any contribution being elevated beyond its tier?** If yes, the report has been laundered. The report must be regenerated with proper tier preservation.
- **Conversely, when can a report be published without per-contribution signatures?** Only when all contributions come from agents within the same privilege tier (no laundering possible), or the report body is explicitly marked as `SYNTHESIZED_FROM_MULTIPLE_TIERS` and includes the full provenance appendix.
Operational Effect
- **Laundering Prevention:** Prevents low-privilege content from being promoted to high-privilege status in reports.
- **Attribution Correctness:** Ensures each claim is attributed to its actual contributor, not the compiler.
- **Compliance Integrity:** Preserves the provenance of every contribution, satisfying audit requirements for mixed-classification reports.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class PrivilegeTier(Enum):
PUBLIC = 1
INTERNAL = 2
CONFIDENTIAL = 3
class LaunderingVerdict(Enum):
CLEAN_ATTRIBUTION = auto()
PRIVILEGE_LAUNDERING = auto()
MISSING_SIGNATURE = auto()
@dataclass(frozen=True)
class Contribution:
contribution_id: str
contributor_pubkey: str
privilege_tier: PrivilegeTier
signature: str | None
@dataclass(frozen=True)
class Report:
report_id: str
compiler_pubkey: str
contributions: tuple[Contribution, ...]
@dataclass(frozen=True)
class LaunderingManifest:
verdict: LaunderingVerdict
directive: str
manifest_digest: str
class AuthorityLaunderingGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_report(
cls, report: Report, reported_tier: PrivilegeTier
) -> LaunderingManifest:
# Gate 1: Signature presence
for c in report.contributions:
if not c.signature:
return LaunderingManifest(
verdict=LaunderingVerdict.MISSING_SIGNATURE,
directive=f"HALT: Contribution {c.contribution_id} lacks signature.",
manifest_digest=cls._hash(f"NO_SIG:{c.contribution_id}")
)
# Gate 2: Privilege laundering
max_contribution_tier = max((c.privilege_tier.value for c in report.contributions), default=0)
if reported_tier.value > max_contribution_tier:
# Report claims higher privilege than any contribution justifies
return LaunderingManifest(
verdict=LaunderingVerdict.PRIVILEGE_LAUNDERING,
directive=f"HALT: Report claims {reported_tier.name} but highest contribution is tier {max_contribution_tier}.",
manifest_digest=cls._hash(f"LAUNDER:{reported_tier.value}:{max_contribution_tier}")
)
# Gate 3: Clean
digest = cls._hash(json.dumps({
"report": report.report_id,
"compiler": report.compiler_pubkey,
"reported_tier": reported_tier.name,
"contributions": tuple(sorted(c.contribution_id for c in report.contributions)),
"max_contribution_tier": str(max_contribution_tier)
}, sort_keys=True))
return LaunderingManifest(
verdict=LaunderingVerdict.CLEAN_ATTRIBUTION,
directive=f"PROCEED: Report tier {reported_tier.name} justified by contribution tier {max_contribution_tier}.",
manifest_digest=digest
)