Python Middleware Example¶
This is the copyable stdlib-only integration shape — the packaged SDK is also live (pip install planisphere). The helper lives at docs/examples/python/planisphere_gate.py and uses
only the Python standard library.
Use it at the side-effect boundary: before an AI workflow files, sends, exports, posts, deploys, buys, deletes, or calls a tool that mutates a customer system.
Install Shape¶
Copy docs/examples/python/planisphere_gate.py into the service that owns the
AI workflow. Configure:
base_url: Planisphere API host.api_key: tenant API key.law_context: matter/tool/doctrine metadata for the action.source_key: stable source key from your workflow state.
Generic Gate¶
from planisphere_gate import PlanisphereClient, PlanisphereNeedsReview, route_for_review
client = PlanisphereClient(
base_url="https://api.planisphere.ooo",
api_key="<tenant-api-key>",
)
def guarded_action():
try:
decision = client.require_allow(
surface="agent-service",
source_key="matter-safe:motion-draft-123",
proposed_action="File an AI-drafted motion with unverified citations.",
law_context={
"matter_band": "litigation",
"tool_id": "generic-frontier-model",
"doctrine_anchor": "fre-707",
"jurisdiction": "federal",
},
redacted_text="AI-drafted filing contains citations requiring review.",
)
except PlanisphereNeedsReview as review:
return route_for_review(review.decision)
execute_side_effect()
return {"status": "executed", "planisphere": decision}
What The Helper Preserves¶
- Raw prompt/document content stays out of durable evidence by default.
needs_reviewbecomes a pause/resume object instead of a dead-end block.catalog_refsstay attached to the paused action so the team can see which SKU/product atoms are being invoked.evidence_packet.hrefgives the workflow a durable receipt link.
Verify The Receipt Seal¶
The helper can also submit the raw-safe packet fields to Planisphere's public verifier. That proves the returned seal matches the decision metadata without shipping Planisphere's private scoring, policy, or signing logic into the customer service.
verification = client.verify_evidence_seal(decision)
if not verification["verified"]:
raise RuntimeError("Planisphere evidence seal verification failed.")
Bind Law Review Grades To The Mirror¶
For law review decisions, the helper carries a pinned public mirror rubric. It
computes the same digest as GET /law/grading-mirror, normalizes categorical
grades locally, and posts the full mirror tuple with the review decision. Raw
legal text does not go into the mirror tuple.
from planisphere_gate import canonical_law_mirror_digest, law_mirror_review_fields
mirror_grades = {
"citation_support": "PARTIAL",
"privilege_boundary": "PASS",
"supervision_route": "PASS",
"client_confidentiality": "PASS",
"filing_readiness": "PARTIAL",
}
assert canonical_law_mirror_digest()
fields = law_mirror_review_fields(mirror_grades)
review = client.review_decision(
action_key=decision["action_key"],
reviewer="partner-or-gc",
decision="escalated",
reason="Citation support needs partner review.",
mirror_grades=mirror_grades,
)
assert review["mirror_seal"]["mirror_digest"] == fields["mirror_digest"]
mirror_verification = client.verify_law_mirror_seal(review)
if not mirror_verification["verified"]:
raise RuntimeError("Planisphere law mirror seal verification failed.")
Checked Responses¶
Use generated examples for docs and website snippets:
docs/examples/generated/law-proposed-action.response.jsondocs/examples/generated/law-grading-mirror.response.jsondocs/examples/generated/evidence-seal-verify.response.jsondocs/examples/generated/law-mirror-seal-verify.response.jsondocs/examples/generated/active-manifest.response.jsondocs/examples/generated/evidence-packet.response.json
Regenerate them with:
.venv/bin/python scripts/export_docs_examples.py
Payload Retention: Recording Completed Acts¶
POST /v1/record (and /v1/record/batch) attests acts that already happened —
an output was marked, a disclosure was shown. Planisphere stores hash
commitments only — retain your payload to re-prove WHAT was recorded. Send
digests in source_payload (for example content_sha256); raw content is
rejected at the edge and is never stored. Every record response repeats this in
its payload_retention field. Wire your integration to archive the payload
bytes (or a durable pointer to them) alongside the returned action_key: the
sealed receipt proves a payload with that hash was recorded, when, and for whom
— only your retained copy proves what the payload said.
For a period export, page GET /tenant/exports/records?from=&to=&cursor= (the
signed recorded_at is the filter; unknown query parameters are rejected, never
silently ignored), then POST the action keys to
/evidence-packets/seal-bundle/batch as {"action_keys": [...]} (a wrapped
object — unlike POST /v1/record/batch, which takes a bare JSON array of
record bodies) for one offline-verifiable evidence package, and fetch
GET /v1/records/{action_key}/inclusion-proof for the canonical roll-up
inclusion proof. Records anchor on the hourly roll-up epoch, so a
just-sealed record returns an honest 409 with an estimated_next_epoch_at
hint until the next epoch.