Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,22 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Run CI (install, lint, typecheck, tests, evals)
run: bash scripts/ci.sh
run: bash scripts/ci.sh

live-ecosystem:
runs-on: ubuntu-latest
if: vars.PF_CORE_REPO_PATH != '' || vars.PCS_CORE_REPO_PATH != '' || vars.AKTA_REPO_PATH != ''
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install SCOPE
run: pip install -e ".[dev]"
- name: Live contract tests
env:
PF_CORE_REPO_PATH: ${{ vars.PF_CORE_REPO_PATH }}
PCS_CORE_REPO_PATH: ${{ vars.PCS_CORE_REPO_PATH }}
AKTA_REPO_PATH: ${{ vars.AKTA_REPO_PATH }}
run: pytest tests/test_live_contracts.py -m live_contract -v
51 changes: 27 additions & 24 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,43 @@

| Version | Supported | Notes |
| ------- | --------- | ----- |
| 1.0.x | Yes | Stable contract; security advisories for 1.0.x |
| 0.11.x | Yes | Workflow release; migrate to 1.0 for contract freeze |
| 0.10.x | Yes | Production-trust adapters |
| 0.9.x | Yes | AKTA session-complete integration |
| 0.8.x | Yes | Split AKTA summary contracts, pilot fixtures |
| 0.7.x | Limited | Critical fixes only |
| < 0.7 | No | Upgrade required |
| 0.8.x | Yes | Current release line; security fixes backported here |
| 0.7.x | Best effort | Prior institutional pilot line; upgrade to 0.8.x recommended |
| 0.6.x and earlier | No | Unsupported; no security patches |

Report issues against the latest **0.8.x** release on [main](https://github.com/fraware/SCOPE).

## Reporting a vulnerability

Report security issues privately to the repository maintainers via GitHub Security Advisories on [fraware/SCOPE](https://github.com/fraware/SCOPE).
Report security issues privately via GitHub Security Advisories on [fraware/SCOPE](https://github.com/fraware/SCOPE/security/advisories/new). Do not open public issues for undisclosed vulnerabilities.

## v0.8 security model

SCOPE v0.8.x builds on schema validation, canonical hashing, hash-chained ledger events, explicit expiration checks, and fail-closed behavior for unknown scopes, invalid roles, and forbidden queue transitions.

Include: affected version, reproduction steps, impact assessment, and suggested fix if available.
**Cryptography and signing**

## Security model (v0.8+)
- Ed25519 signatures on decisions and grants when production signing is enabled (`SCOPE_PRODUCTION_MODE`, minimum signing assurance policy in `policy/minimum_signing_assurance.yaml`)
- Signing assurance levels (SAL0–SAL4) with registry key binding and reviewer public-key references; external HSM/KMS interface documented for SAL4 (`docs/signing_assurance.md`, `docs/key_management.md`)
- Combined `scope_trust_root_hash` ties policy and reviewer registry integrity into decision, grant, and PCS export provenance

SCOPE provides layered institutional trust boundaries:
**Identity**

- **Identity assurance (IAL0–IAL4)**: OIDC/JWT verification, optional SAML assertion verify, org RBAC mapping. Production mode rejects caller-supplied identity JSON without verified tokens (`SCOPE_PRODUCTION_MODE=1`).
- **Signing assurance (SAL0–SAL4)**: Ed25519 local keys, registry-backed keys, and reference KMS/HSM adapters. Minimum SAL enforced per scope in `policy/minimum_signing_assurance.yaml`.
- **Authority provenance**: Two-stage RBAC + SCOPE authority checks with explicit `authority_checks` on decisions and grants.
- **Ledger integrity**: Hash-chained local JSONL, optional remote/WORM sinks with `fail_closed` delivery for high-risk events.
- **AKTA contract**: Split summary schemas (`completed` vs `session_required`); verifiable pilot fixture pack.
- Identity assurance levels (IAL0–IAL4) with provenance on decisions and session grants (`scope/identity_assurance.py`, `docs/identity_assurance.md`)
- Optional OIDC/JWT verification (`SCOPE_OIDC_*`, `scope identity verify-token`) for institutional identity claims; org RBAC in `policy/org_rbac.yaml` is separate from SCOPE scope authority

SCOPE does **not** replace IRB, biosafety, EHS, or certify scientific safety. See [docs/limitations.md](docs/limitations.md).
**Ledger and delivery**

## Cryptography
- Local hash-chained JSONL ledger with verification APIs
- Optional remote HTTP append sink (`SCOPE_LEDGER_REMOTE_URL`); delivery semantics `best_effort`, `at_least_once` (spool), and `fail_closed` for high-risk grant issuance when remote delivery is required
- Runtime violation and expiration events for PF feedback loops; remote sink is not a WORM or authoritative tamper-evident store

- Decision and grant signatures use **Ed25519** (local PEM or registry-backed keys).
- OIDC tokens verified via JWKS (RS256) or static public key.
- Ledger events use SHA-256 hash chaining.
**AKTA review contract**

## Operational hardening
- Signed `summary.json` artifacts validated against split schemas for `completed` vs `session_required` (`scope-akta-review-v0.8.1`); consumers must branch on `summary.status`

For production deployment guidance see [docs/production_deployment.md](docs/production_deployment.md) and [docs/trusted_boundary.md](docs/trusted_boundary.md).
**Known limits**

Threat model details: [docs/threat_model.md](docs/threat_model.md).
- No live SAML/SCIM directory sync; RBAC and identity mapping are file-based
- Reviewer judgment, domain safety, and physical lab safety are out of scope
- See [docs/threat_model.md](docs/threat_model.md), [docs/trusted_boundary.md](docs/trusted_boundary.md), and [docs/limitations.md](docs/limitations.md) for residual risk and deployment boundaries.
67 changes: 67 additions & 0 deletions adapters/vsa/fetch_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

from __future__ import annotations

import argparse
import json
import os
import time
import urllib.error
import urllib.request
from collections.abc import Callable
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -59,3 +62,67 @@ def fetch_vsa_report_from_file_or_url(
if text.startswith("http://") or text.startswith("https://"):
return fetch_vsa_report(url=text, token=token)
return import_vsa_report(source)


def schedule_vsa_refetch(
*,
report_id: str | None = None,
url: str | None = None,
interval_seconds: int = 3600,
on_refresh: Callable[[dict[str, Any]], None] | None = None,
max_iterations: int | None = None,
token: str | None = None,
) -> None:
"""
Scheduled re-fetch hook for evidence refresh pipelines.

Downstream systems should compare ``evidence_summary.overall_state`` across
fetches and invoke grant expiration rules when evidence downgrades.
"""
iteration = 0
while max_iterations is None or iteration < max_iterations:
report = fetch_vsa_report(url=url, report_id=report_id, token=token)
if on_refresh is not None:
on_refresh(report)
iteration += 1
if max_iterations is not None and iteration >= max_iterations:
break
time.sleep(max(interval_seconds, 1))


def _cli_main() -> int:
parser = argparse.ArgumentParser(description="Fetch or schedule VSA report re-fetch.")
parser.add_argument("--url", default=None)
parser.add_argument("--report-id", default=None)
parser.add_argument(
"--interval", type=int, default=0, help="Seconds between fetches (0 = once)."
)
parser.add_argument("--once", action="store_true", help="Fetch once and exit.")
parser.add_argument("--out", type=Path, default=None, help="Write fetched report JSON.")
args = parser.parse_args()

def _emit(report: dict[str, Any]) -> None:
if args.out:
args.out.parent.mkdir(parents=True, exist_ok=True)
args.out.write_text(
json.dumps(report, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
else:
print(json.dumps(report, indent=2))

if args.once or args.interval <= 0:
_emit(fetch_vsa_report(url=args.url, report_id=args.report_id))
return 0

schedule_vsa_refetch(
report_id=args.report_id,
url=args.url,
interval_seconds=args.interval,
on_refresh=_emit,
)
return 0


if __name__ == "__main__":
raise SystemExit(_cli_main())
134 changes: 134 additions & 0 deletions docs/ecosystem_demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Live Ecosystem Demo (AKTA → SCOPE → PF → PCS)

This document describes the **live** cross-repo authorization loop using sibling repositories and optional REST triggers. For fixture-only walkthroughs, see [akta_scope_demo.md](akta_scope_demo.md).

Related: [external_integration_contracts.md](external_integration_contracts.md), [pf_core_bridge.md](pf_core_bridge.md), [pcs_export.md](pcs_export.md).

## Prerequisites

```powershell
pip install -e ".[dev]"
```

Configure partner repo paths (local checkouts):

| Variable | Purpose |
|----------|---------|
| `AKTA_REPO_PATH` | AKTA runtime checkout (optional; demo uses bundled fixtures) |
| `PF_CORE_REPO_PATH` | PF-Core validator for `--live` PF export |
| `PCS_CORE_REPO_PATH` | PCS validator for `--live` PCS export |
| `SCOPE_REST_URL` | SCOPE REST base URL when using REST trigger (default `http://127.0.0.1:8765`) |
| `SCOPE_API_KEY` | Bearer token when REST auth is enabled |

## One-command demo

**Linux / macOS:**

```bash
export PF_CORE_REPO_PATH=/path/to/pf-core # optional live validation
export PCS_CORE_REPO_PATH=/path/to/pcs-core
bash scripts/ecosystem_demo.sh
```

**Windows (PowerShell):**

```powershell
$env:PF_CORE_REPO_PATH = "C:\path\to\pf-core"
$env:PCS_CORE_REPO_PATH = "C:\path\to\pcs-core"
.\scripts\ecosystem_demo.ps1
```

Output directory defaults to `/tmp/scope_ecosystem_demo` (Linux) or `%TEMP%\scope_ecosystem_demo` (Windows).

## Demo flow

```mermaid
flowchart LR
AKTA[AKTA_trigger_record] --> SCOPE[scope_akta_review]
SCOPE --> PF[export_pf_live]
PF --> VIOL[pf_inject_violation]
VIOL --> LEDGER[scope_ledger]
SCOPE --> PCS[export_pcs_live]
LEDGER --> QUAL[quality_report]
```

1. **AKTA → SCOPE** — `scope akta review` (CLI) or `scripts/akta_rest_review.py` (REST)
2. **SCOPE → PF** — `scope export pf --validate --live`
3. **PF violation loop** — `scripts/pf_inject_violation.py` simulates a blocked tool call and records `runtime_scope_violation`
4. **SCOPE → PCS** — `scope export pcs --validate --live`
5. **Quality metrics** — `scope quality report` with non-zero `post_approval_runtime_violation_rate`

## REST trigger path

Start the REST server:

```bash
uvicorn adapters.generic_rest.server:app --host 127.0.0.1 --port 8765
```

Run demo with REST:

```bash
USE_REST=true SCOPE_REST_URL=http://127.0.0.1:8765 bash scripts/ecosystem_demo.sh
```

Or call the AKTA wrapper directly:

```bash
python scripts/akta_rest_review.py \
--akta-record examples/protocol_drift/akta_record.json \
--akta-trigger examples/protocol_drift/review_trigger.json \
--reviewer examples/protocol_drift/reviewer_protocol_owner.json \
--grant-scope protocol_draft \
--decision-rationale "REST demo approval" \
--out-dir /tmp/akta_rest_out
```

## Session-complete path

Multi-role demo with votes manifest:

```bash
scope akta review \
--akta-record examples/pilot/multi_role_genomics_review/akta_record.json \
--akta-trigger examples/pilot/multi_role_genomics_review/review_trigger.json \
--session-complete \
--votes examples/pilot/multi_role_genomics_review/votes.json \
--grant-scope genomics_analysis \
--out-dir /tmp/multi_role_demo
```

REST equivalent: pass `--session-complete` and `--votes` to `scripts/akta_rest_review.py`.

## VSA scheduled re-fetch

Evidence downgrade can trigger grant expiration via existing expiration rules. Schedule periodic VSA re-fetch:

```python
from adapters.vsa.fetch_report import schedule_vsa_refetch

schedule_vsa_refetch(
report_id="VSA-REPORT-001",
interval_seconds=3600,
on_refresh=lambda report: print(report["evidence_summary"]),
)
```

Or run the CLI hook:

```bash
python -m adapters.vsa.fetch_report --report-id VSA-REPORT-001 --interval 3600 --once
```

Set `VSA_API_URL` and `VSA_API_TOKEN` for live fetches.

## Acceptance criteria

- One command runs the full loop locally
- `post_approval_runtime_violation_rate` > 0 after PF inject
- PF/PCS live validators pass when repo paths are set
- Skips explicitly when paths absent (default CI remains green)

## CI

Optional `live-ecosystem` job in `.github/workflows/ci.yml` runs when repository variables are configured. See [CONTRIBUTING.md](../CONTRIBUTING.md).
44 changes: 31 additions & 13 deletions docs/external_integration_contracts.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# External Integration Contracts (v0.7)
# External Integration Contracts (v0.9)

Formal field-mapping contracts for AKTA, PF-Core, PCS, and VSA integrations. These contracts define the shapes SCOPE produces or consumes locally; external repositories must implement compatible endpoints or adapters.

Expand Down Expand Up @@ -60,7 +60,7 @@ Evidence vocabulary mapping: [evidence_vocab_mapping.md](evidence_vocab_mapping.
| `scope_grant.json` | Issued grant (`completed` only) |
| `summary.json` | Adapter summary; schema selected by `summary.status` |

Contract version: `scope-akta-review-v0.8.1`. Branch on `summary.status`:
Contract version: `scope-akta-review-v0.9` (compatible with v0.8.1+ consumers). Branch on `summary.status`:

| `summary.status` | Schema |
|------------------|--------|
Expand Down Expand Up @@ -208,27 +208,45 @@ REST: full queue lifecycle under `/v0/review-queue/{id}/...`.

Quality metrics: `open_queue_count`, `overdue_queue_count` (open statuses include `in_review`, `needs_information`, `escalated`).

## Version alignment (v0.7)
## Version alignment (v0.9)

| Artifact | Version field | Expected value |
|----------|---------------|----------------|
| SCOPE package | `pyproject.toml` / `scope/_version.py` | `0.7.0` |
| SCOPE packet | `packet_version` | `0.7.0` |
| SCOPE grant | `grant_version` | `0.7.0` |
| Quality report | `report_version` | `0.7` |
| Review queue | `queue_version` | `0.7.0` |
| AKTA review summary | `adapter_contract_version` | `scope-akta-review-v0.7` |
| SCOPE package | `pyproject.toml` / `scope/_version.py` | `0.9.0` |
| SCOPE packet | `packet_version` | `0.9.0` |
| SCOPE grant | `grant_version` | `0.9.0` |
| Quality report | `report_version` | `0.8` |
| Review queue | `queue_version` | `0.9.0` |
| AKTA review summary | `adapter_contract_version` | `scope-akta-review-v0.9` |
| PF obligation | `obligation_version` | `pf-core-v0.5` |
| PCS manifest | `manifest_version` | `pcs-v0.5` |
| Policy bundle | `version` in YAML | `scope-core-v0.7` |
| Policy bundle | `version` in YAML | `scope-core-v0.9` |

## Environment paths and live demo

Partner repositories are referenced via environment variables (never committed):

| Variable | Required for | Example |
|----------|--------------|---------|
| `AKTA_REPO_PATH` | AKTA-side tooling / optional CI | `/opt/akta` |
| `PF_CORE_REPO_PATH` | PF live validation + violation loop | `/opt/pf-core` |
| `PCS_CORE_REPO_PATH` | PCS live validation | `/opt/pcs-core` |
| `SCOPE_REST_URL` | REST demo (`scripts/akta_rest_review.py`) | `http://127.0.0.1:8765` |
| `SCOPE_API_KEY` | Authenticated REST | (institutional secret) |
| `VSA_API_URL` | Scheduled VSA re-fetch | `https://vsa.example/api` |
| `VSA_API_TOKEN` | VSA bearer auth | (institutional secret) |

One-command ecosystem demo: [ecosystem_demo.md](ecosystem_demo.md).

PF violation feedback uses `scripts/pf_inject_violation.py` (loads `adapters/pf_core/export_obligation.py` output, records `runtime_scope_violation` via CLI or `POST /v0/ledger/violations`).

## External repo dependencies

These integrations require live services or repositories not present in this repo:

- **AKTA**: authoritative admissibility decisions and nested record storage
- **PF-Core**: runtime obligation enforcement at tool invocation
- **PCS**: release pipeline ingestion and institutional signing workflows
- **PF-Core**: runtime obligation enforcement at tool invocation (`pf-core-v0.5`)
- **PCS**: release pipeline ingestion and institutional signing workflows (`pcs-v0.5`)
- **VSA**: live ScientificReport generation from validation pipelines

Local adapters validate shapes and hashes by default. Cross-repo end-to-end tests run when env paths are configured (`tests/test_live_contracts.py`).
Local adapters validate shapes and hashes by default. Cross-repo end-to-end tests run when env paths are configured (`tests/test_live_contracts.py`, optional CI job `live-ecosystem`).
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "scope-protocol"
version = "0.8.1"
version = "0.9.0"
description = "Scoped Scientific Authorization Protocol for AI-shaped science"
readme = "README.md"
license = { text = "MIT" }
Expand Down
2 changes: 1 addition & 1 deletion scope/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Package version (single source of truth)."""

__version__ = "0.8.1"
__version__ = "0.9.0"
Loading
Loading