Provenance

Neuron disclosure

A public trace share masks your account's private neurons and your canary-set members as opaque n_… ids. Neuron disclosure lets one specific person, such as an auditor, a regulator or a customer's safety reviewer, ask to see them. You decide who sees what, and for how long.

Requesting is free. Granting is a Growth feature; Developer accounts get a monthly trial allowance (trace_disclosure_grant, 10 grants).

Choose a mode

| Mode | What happens to a request | | --- | --- | | approve_each (default) | It waits in your inbox until you approve or deny it. | | auto | Any verified requester is granted right away, for your default duration. You're still notified and can revoke. | | never | The requester sees that you don't accept disclosure requests. No request is stored. |

Set it in the dashboard (Account → Neuron disclosure) or with the API. An API key needs the admin scope; a dashboard user needs the owner or admin role.

curl -X PUT $API/v1/account/trace-disclosure -H "Authorization: Bearer $PRV_ADMIN_KEY" \
  -H "Content-Type: application/json" -d '{"mode":"approve_each","default_ttl_days":7}'
# → {"mode":"approve_each","default_ttl_days":7,"max_ttl_days":30,"curated_sets_disclosable":false}

How a request works

  1. The requester opens your share link (/trace/share/:token) and selects Request details.
  2. They enter their email and open the verification link, then create or use a passkey.
  3. They read the terms, write a reason, and send the request. They can ask for the whole share or pick specific masked neurons.
  4. You're notified in the dashboard (Trace → Disclosure), by email, in Slack if connected, and by the trace.disclosure_requested webhook.
  5. You approve (optionally with a shorter duration) or deny. On approval, trace.disclosure_granted fires.
  6. The requester returns to the share page and sees the covered neurons: layer, index, pole, label and top examples, with the watermark across the view and on every copied row.

When the grant expires or you revoke it, the share renders masked again. If you revoke the share link itself, the grant shows nothing.

Review requests with the API

curl "$API/v1/trace/disclosure-requests?status=pending" -H "Authorization: Bearer $PRV_KEY"
# → {"mode":"approve_each","data":[{"request_id":"dsr_…","status":"pending","scope_kind":"share",
#     "reason":"Independent audit of the injection guard","requester":{"email":"auditor@example.org", …}, …}]}

curl -X POST $API/v1/trace/disclosure-requests/dsr_…/approve -H "Authorization: Bearer $PRV_KEY" \
  -H "Content-Type: application/json" -d '{"ttl_days":3}'
curl -X POST $API/v1/trace/disclosure-requests/dsr_…/revoke -H "Authorization: Bearer $PRV_KEY"
curl $API/v1/trace/disclosure-requests/dsr_…/views -H "Authorization: Bearer $PRV_KEY"
# → {"request_id":"dsr_…","data":[{"view_id":"…","viewed_at":"…","viewer_email":"auditor@example.org","ip_hash":"…"}]}
import { Provenance } from "@provenance/sdk";

const prov = new Provenance({ apiKey: process.env.PRV_KEY! });
for (const r of await prov.trace.disclosure.list({ status: "pending" })) {
  console.log(r.requester.email, r.reason);
  await prov.trace.disclosure.approve(r.requestId, { ttlDays: 3 });
}

In Python: prov.trace.disclosure.list(status="pending"), .approve(id, ttl_days=3), .deny(id), .revoke(id), .views(id), .get_mode() and .set_mode(mode=…).

| Endpoint | | | --- | --- | | GET / PUT /v1/account/trace-disclosure | Read or set mode and default_ttl_days. | | GET /v1/trace/disclosure-requests | Your inbox, newest first. Filter with ?status= (pending, granted, denied, revoked or expired). | | POST /v1/trace/disclosure-requests/:id/approve | Grant a pending request. Body {ttl_days?} (1–30). | | POST /v1/trace/disclosure-requests/:id/deny | Deny a pending request. | | POST /v1/trace/disclosure-requests/:id/revoke | End an active grant now. | | GET /v1/trace/disclosure-requests/:id/views | The append-only view log for one request. |

Deciding on a request that isn't pending (or revoking one that isn't granted) is a 403 disclosure_not_permitted. Another account's request is a 404 resource_missing. The requester endpoints under /v1/trace/disclosure/* accept only a requester session; anything else is a 401 disclosure_requires_human.

Manifests are unchanged

A disclosed view is a render for one person, not a new artifact. The share's signed trace manifest still binds public_graph_hash to the masked public render, so offline verification works the same for everyone.