Metadata-Version: 2.4
Name: prx-spec
Version: 0.2.0
Summary: Parallect Research eXchange (.prx) format specification and bundle library
License-Expression: MIT
Requires-Dist: pydantic>=2.0,<3.0
Requires-Dist: pynacl>=1.5.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# prx-spec

[![PyPI version](https://img.shields.io/pypi/v/prx-spec.svg)](https://pypi.org/project/prx-spec/)
[![Python](https://img.shields.io/pypi/pyversions/prx-spec.svg)](https://pypi.org/project/prx-spec/)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![CI](https://github.com/parallect/prx-spec/actions/workflows/ci.yml/badge.svg)](https://github.com/parallect/prx-spec/actions/workflows/ci.yml)

**Reference implementation of the `.prx` file format — the open standard for packaging multi-provider AI research into portable, verifiable bundles.**

A `.prx` file is a gzipped tar archive containing markdown reports, structured claims with typed categories, source registries, evidence graphs, W3C PROV-O provenance, and Ed25519 cryptographic attestations. Think of it as "a research paper in a zip file" that any tool can read, write, and verify.

Every file inside a bundle is human-readable text or JSON. You can `tar -xzf` a `.prx` and read it in any text editor.

## What's in a bundle

```
prx_a1b2c3d4/
  manifest.json          # JSON-LD envelope (@context, @type: prov:Entity), content flags
  query.md               # The original research question
  providers/
    perplexity/
      report.md          # Full provider report
      meta.json          # Cost, tokens, response_hash, raw_response_size
    gemini/report.md
    openai/report.md
  synthesis/
    report.md            # Unified synthesis across providers
    claims.json          # Extracted claims with claim_type + provider attribution
  sources/
    registry.json        # Deduplicated, quality-scored sources
  evidence/
    graph.json           # Claim-source graph
    claim_relations.json # Claim-to-claim relations (supports / contradicts / refines)
  provenance/
    graph.jsonld         # W3C PROV-O provenance graph
  attestations/
    <subject>.json       # Optional per-file Ed25519 signatures
```

## Install

```bash
pip install prx-spec
```

Or with [uv](https://docs.astral.sh/uv/):

```bash
uv add prx-spec
```

Requires Python 3.10+.

## Usage

### Read a bundle

```python
from prx_spec import read_bundle

bundle = read_bundle("research.prx")

print(bundle.manifest.query)
print(bundle.manifest.providers_used)

for provider in bundle.providers:
    print(f"{provider.name}: {len(provider.report_md)} chars")

if bundle.synthesis_md:
    print(bundle.synthesis_md[:200])
```

### Write a bundle

```python
from prx_spec import BundleData, ProviderData, write_bundle
from prx_spec.models import Manifest, Producer

manifest = Manifest(
    id="prx_a1b2c3d4",
    query="What are the tradeoffs of consensus algorithms?",
    created_at="2026-04-16T10:30:00Z",
    producer=Producer(name="my-tool", version="0.1.0"),
    providers_used=["perplexity", "gemini"],
    has_synthesis=False,
    has_claims=False,
    has_sources=False,
    has_evidence_graph=False,
    has_follow_ons=False,
)

bundle = BundleData(
    manifest=manifest,
    query_md="# Research Query\n\nWhat are the tradeoffs of consensus algorithms?",
    providers=[
        ProviderData(name="perplexity", report_md="# Perplexity Report\n\n..."),
        ProviderData(name="gemini", report_md="# Gemini Report\n\n..."),
    ],
)

write_bundle(bundle, "research.prx")
```

### Validate a bundle

Three validation levels:

- **L0 (structural)** — valid archive, required files present
- **L1 (manifest conformance)** — providers listed in manifest match directories on disk
- **L2 (cross-reference)** — claims reference real providers, sources are internally consistent

```python
from prx_spec import validate_archive, validate_bundle

# Validate a .prx file on disk
result = validate_archive("research.prx")

# Or validate an in-memory bundle
result = validate_bundle(bundle)

print(result.valid)
for level in result.levels:
    status = "pass" if level.passed else "fail"
    print(f"L{level.level}: {status}")
    for error in level.errors:
        print(f"  error: {error}")
```

### Sign and verify attestations

Ed25519 signatures for bundle provenance. Each attestation covers a specific subject (a manifest hash, a provider report hash, etc.) and carries a signer identity.

```python
from prx_spec import generate_keypair, sign_attestation, verify_attestation
from prx_spec.models import Attestation, Signer, Subject

# Generate a signing keypair (once)
signing_key, verify_key, key_id = generate_keypair()
print(key_id)  # prx_pub_a1b2c3d4e5f6...

# Build an attestation over a file's SHA-256 hash
attestation = Attestation(
    type="bundle",
    signer=Signer(type="researcher", key_id=key_id),
    subject=Subject(file="manifest.json", sha256="abc123..."),
)

signed = sign_attestation(attestation, signing_key)
assert verify_attestation(signed, verify_key) is True
```

See [`docs/best-practices.md`](docs/best-practices.md) for the full attestation model, including response-hash chain-of-custody recommendations.

### Merge and disagreement

Two bundles answering the same query can be merged. Providers, claims, sources, and evidence are deduplicated; claim-level conflicts are surfaced.

```python
from prx_spec import merge_bundles, compute_disagreement

merged, stats = merge_bundles([bundle_a, bundle_b])
disagreement = compute_disagreement(merged)

print(f"Providers agreeing: {disagreement.consensus_count}")
print(f"Providers conflicting: {disagreement.conflict_count}")
```

## Pydantic models

All format structures are Pydantic v2 models with full type annotations. Use them directly for custom tooling:

```python
from prx_spec.models import (
    Manifest, Producer, ProviderBreakdown,
    Citation, ProviderMeta, TokenUsage,
    BasicClaim, EnhancedClaim, ClaimsFile, FollowOn,
    Source, SourcesRegistry,
    EvidenceCluster, EvidenceClusters, EvidenceEdge, EvidenceGraph,
    Attestation, Signer, Subject,
    ContinuationEntry, Continuations,
    MergeConflict, MergeResult, MergeStats,
)
```

Every model accepts `x-` prefixed extension keys via `model_config = ConfigDict(extra="allow")`.

## JSON Schemas

Pre-exported JSON Schemas live in `schemas/v1/` for non-Python tools:

```
schemas/v1/
  manifest.json
  citations.json
  provider-meta.json
  claims.json
  claim-relations.json
  follow-ons.json
  sources-registry.json
  evidence-graph.json
  evidence-clusters.json
  continuations.json
  attestation.json
  context.jsonld        # JSON-LD context for the @vocab/prov/schema/prx namespaces
```

Regenerate them from the Pydantic models:

```python
from prx_spec.schemas import export_schemas
export_schemas()
```

## Best practices

See [`docs/best-practices.md`](docs/best-practices.md) for recommended implementation patterns, including:

- **Response hashing** — hash raw provider responses at receipt time (SHA-256) and record them in `input_report_hashes`. Since no AI provider signs text responses today, this is the strongest practical measure for establishing chain of custody.
- **Claim type classification** — use the standardized taxonomy from `prx_spec.claim_types` for consistent claim categorization.
- **Provenance graphs** — include W3C PROV-O provenance graphs for full research lineage.
- **Attestation signing** — sign bundle attestations with Ed25519 for verifiable provenance.

## Development

```bash
git clone https://github.com/parallect/prx-spec.git
cd prx-spec
uv sync --group dev
uv run pytest
uv run ruff check .
```

## Contributing

Contributions welcome — bug reports, spec feedback, and implementations in other languages are all appreciated. For security reports, email `security@parallect.ai`.

## License

MIT — see [LICENSE](LICENSE).

---

Built by [SecureCoders](https://securecoders.com). The primary reference producer of `.prx` bundles is [`parallect`](https://github.com/parallect/parallect), our open-source multi-provider research CLI. A hosted managed version is available at [parallect.ai](https://parallect.ai).
