Data
Conversation records are folded into signed tables before you can query their messages, tool calls, approvals, and payments. Data exposes those tables read-only, restricted to what your credential admits.
The signed, append-only log records conversation events. The projector folds those events into tables you can query with SQL — a query reads the tables, never the log, and cannot write to either.
conv-aIn scope- Turn starts
- Turn completes
conv-bIn scope- Turn starts
- No completion
conv-cOutside your scope- Turn starts
- Turn completes
Shared folds turn log records into typed rows ahead of any query. The projection adds the partition, source incarnation, position, and turn id.
partition · source_incarnation · position · turn_idThe log stays unchanged, and an unfinished turn folds to nothing — conv-b yields no rows for anyone.
SELECT partition, position, role, text
FROM messages
ORDER BY partition, position;| partition | position |
|---|---|
conv-a | 1 |
conv-b is in scope, but its turn is unfinished, so it folded to no rows. conv-c is outside your scope. Only the row from conv-a reaches this result.
Shared event interpretation
Decoding reads an event's payload. A fold interprets events to derive a fact, such as a settled payment. Separate implementations can disagree about which events count.
A decoded payment receipt does not establish that its signature is valid. If a dashboard skips verification, it can display a forged receipt as settled. A spending check that verifies signatures would reject the same receipt.
polyc-facts provides shared folds for readers. Spending checks call a fold directly, and the projector uses the same fold to build the tables a query reads. Both therefore apply the same payment rules.
The tables
The projection adds identifying columns no log event carries:
partition— the conversation the row belongs to, writtenconv-{id}.source_incarnation— which incarnation of the source partition produced the row.position— the row's position in append-only order.turn_id— the turn the row belongs to. The log records it three different ways. The fold normalizes them into one column.
These columns identify rows folded from individual log events. Deployment-wide tables — persona records, credential history, query audit — carry their own keys instead. Start with turns, which every participant grant can name:
-- Inspect committed turns
SELECT * FROM turns LIMIT 5;
-- Count input and output tokens per turn
SELECT turn_id,
SUM(input_tokens) AS input_tokens,
SUM(output_tokens) AS output_tokens
FROM usage
GROUP BY turn_id
ORDER BY turn_id;
-- Find tool calls without a recorded result
SELECT c.name, c.turn_id, c.position
FROM tool_calls c
LEFT JOIN (SELECT tool_call_id FROM tool_calls WHERE block_type = 'result') r
ON r.tool_call_id = c.tool_call_id
WHERE c.block_type = 'call'
AND r.tool_call_id IS NULL
ORDER BY c.position DESC;A tool call, result, approval, and receipt share a call id. tool_calls and payments name it tool_call_id. approvals names it request_id. Join these columns to inspect the transaction.
-- List settled payments with their approvals
SELECT p.reference, p.amount_base_units, p.asset, a.tool_name, a.outcome
FROM payments p
JOIN approvals a ON a.request_id = p.tool_call_id
ORDER BY p.position DESC;The catalog lists the tables your credential's realm may name. A participant grant names the conversation tables; a fleet grant names all of them. Your scope then bounds which partitions' rows appear. Names outside the realm fail to resolve before execution.
Any scope 37
Available in every session. Rows still follow your conversation scope.
turns 4 key columnsOne committed turn in one conversation partition.
turn_idfirst_positionstart_positioncomplete_position
messages 6 key columnsOne text block of a committed turn.
turn_idroleinternal_onlyincompletetexttrust
A row a turn marks internal_only lands only in the fleet segment, so outside admin this column always reads false.
usage 3 key columnsOne turn's token count.
turn_idinput_tokensoutput_tokens
model_call 4 key columnsOne call to the model inside a turn.
turn_idprovidermodelcaptured_clock_unix_ms
tool_calls 9 key columnsOne tool invocation and its result, as the model saw them.
turn_idtool_call_idblock_typenameargumentsresultfirst_partyinternal_onlytrust
Like messages, internal_only rows land only in the fleet segment and read false outside admin.
turn_failed 3 key columnsOne turn that ends in a failure, and the kind of failure.
turn_idfailure_kindmessage
turn_dispatch 6 key columnsOne turn the control plane recorded, with who may see it.
turn_idoccurrencevisibilityvisibility_sourcesource_turn_idedge_asserted_visibility
approvals 10 key columnsOne approval gate a tool call paused on, and its verdict.
turn_idrequest_idtool_nameargs_jsonoutcomeresponse_reasonsignature_statusroutine_granttool_descriptor_hashgrant_scope
Who approved, the caller, and any edited arguments live in the fleet-only approval_details table.
attribution 3 key columnsThe persona that a turn runs as, and each persona that takes part.
turn_idpersona_idrole
The identity assertion's provenance — provider, external id, signing edge — is fleet-only in attribution_provenance.
routine_grant_mutations 7 key columnsOne standing-allowance grant a routine set up, changed, or dropped.
turn_idtool_nametool_descriptor_hashgrant_scopeapprovedresponse_reasonsignature_status
grant_replays 6 key columnsOne capability grant that authorizes one tool call.
turn_idtoolgrant_refcovered_capabilitiescoverage_hashsignature_status
The signing key lives in the fleet-only grant_replay_signers table.
handoffs 10 key columnsOne transfer to another agent, or one refusal to transfer.
turn_idphasechild_conversation_idchild_agent_idcarried_countreasonparent_agent_iddenial_reasonallowedsignature_status
A column the row's phase does not carry reads null: `carried_count` and `child_conversation_id` on a refusal, `allowed`, `parent_agent_id` and `denial_reason` on a transfer. The signing key lives in the fleet-only handoff_signers table.
payments 11 key columnsOne verified inbound payment receipt: what this deployment charged a subject.
subjectamount_base_unitsassetreferencerecipientmethodtool_call_idturn_iddirectionpayer_kindtimestamp_unix
outbound_payments 11 key columnsOne verified outbound receipt: what settled out of a subject's own delegated wallet.
subjectamount_base_unitsassetreferencerecipientmethodtool_call_idturn_iddirectionpayer_kindtimestamp_unix
refusals 9 key columnsOne outbound payment attempt the deployment blocked before it settled.
subjectreasonmerchant_hostrequested_base_unitspermitted_base_unitstool_call_idtimestamp_unixturn_idreason_detail
wallet_link_lifecycle 8 key columnsOne wallet link, renewal, or revocation step.
subjecttransitionassetchain_idlimit_base_unitsperiod_secsexpiry_unixtimestamp_unix
trace_turns 29 key columnsOne turn as a trace row: status, model, usage, and caller persona.
turn_idfirst_positionstatushas_starthas_dispatchedhas_completehas_failedhas_ambiguousfailed_kindfailed_messagefailed_positionhas_failed_positionmodel_providermodel_namesystem_config_reftemperature_microshas_temperaturetop_p_microshas_top_pmax_tokenshas_max_tokensreasoning_leveldispatch_clock_mshas_dispatch_clockusage_inputusage_outputhas_usagecaller_persona_idplannable
trace_steps 7 key columnsOne step in a turn's trace.
ordinalturn_idstep_kindkind_basetrusttruncatedinternal_only
trace_boundaries 8 key columnsOne visibility boundary marker inside a trace.
ordinalmarkeraudienceaudience_sourceedge_assertedsource_turn_idfailed_kindfailed_message
trace_messages 6 key columnsOne message inside a turn's trace.
ordinalturn_idroletexttruncatedinternal_only
trace_tool_calls 10 key columnsOne tool call inside a trace, with its result linkage.
ordinalturn_idtool_call_idnameargumentstruncatedstatusorigin_turn_idresult_turn_idinternal_only
trace_tool_results 9 key columnsOne tool result inside a trace.
ordinalturn_idtool_call_idnameresultfirst_partytruncatedneeds_wallet_linkinternal_only
trace_approvals 11 key columnsOne approval request or decision inside a trace.
ordinalturn_idphaserequest_idtool_nameargsdecisionreasonsignaturesig_validhas_sig_valid
trace_questions 14 key columnsOne structured question the agent asked inside a trace.
ordinalturn_idphasecall_idindexheaderquestionargsdecisionselected_indexhas_selected_indexselected_labelanswered_byverdict
trace_subagents 17 key columnsOne subagent delegation inside a trace.
ordinalturn_idphasesub_agent_idtarget_agent_idtaskprovidermodelsucceededhas_succeedederrorinput_tokensoutput_tokenshas_tokensfirst_partyhas_first_partyverdict
trace_handoffs 10 key columnsOne handoff inside a trace.
ordinalturn_idphasechild_conversation_idchild_agent_idcarried_countreasonparent_agent_iddenial_reasonverdict
trace_payment_attempts 10 key columnsOne payment attempt inside a trace.
ordinalturn_idapproval_poshas_approval_postool_call_idapproved_args_hashurlcurrencychallenge_amountchallenge_id
trace_payment_receipts 15 key columnsOne payment receipt inside a trace.
ordinalturn_iddirectionreferenceamountcurrencyrecipientmethodtool_call_idapproval_poshas_approval_posapproved_args_hashsubjectkindpayer_kind
trace_payment_refusals 10 key columnsOne payment refusal inside a trace.
ordinalturn_idreasonreason_detailmerchant_hostrequested_base_unitspermitted_base_unitstool_call_idsubjecttimestamp
trace_wallet_links 11 key columnsOne wallet-link transition inside a trace.
ordinaltransitionsubjectcurrencychain_idlimit_base_unitslimit_humanperiod_secsexpiry_unixconversation_idtimestamp
trace_records 22 key columnsOne miscellaneous record inside a trace: grants, identity, summary, or marker.
ordinalturn_idrecord_kindtoolgrant_refcoverage_hashverdictidentity_provideridentity_scopeexternal_iddisplay_namepersona_idtexttruncatedcovers_through_positiongate_rejectedgate_countmarkermarker_onhas_marker_onescalated_at_mshas_escalated_at
trace_failures 4 key columnsOne failure record ranked inside a trace.
turn_idrankkindmessage
trace_warnings 4 key columnsOne warning emitted inside a trace.
ordinalcodemessagehas_position
trace_lists 7 key columnsOne list item inside a trace.
owner_kindordinalturn_idlist_kindindextext_valuenumeric_value
memory_portable_facts 8 key columnsOne portable fact: memory a persona allows across conversations.
fact_idtextevent_time_msingested_at_msvalid_from_msconfidence_bpsscopedurability
memory_portable_invalidations 5 key columnsOne invalidation of a portable fact.
fact_idinvalid_at_msreasonsuperseded_by_fact_idfact_scope
memory_portable_lists 5 key columnsOne item in a portable list.
owner_kindowner_idlist_kindindextext_value
Narrowed by scope 7
Your scope determines which partitions' rows appear.
memory_facts 8 key columnsOne private fact a persona's memory extracted.
fact_idtextevent_time_msingested_at_msvalid_from_msconfidence_bpsscopedurability
Only the partition the owning persona owns carries rows for your scope.
memory_invalidations 5 key columnsOne invalidation of a private fact.
fact_idinvalid_at_msreasonsuperseded_by_fact_idfact_scope
memory_lists 5 key columnsOne item in a private list.
owner_kindowner_idlist_kindindextext_value
memory_corroborations 5 key columnsOne corroboration linking a private fact to its origin turn.
fact_idorigin_conversation_idorigin_turn_exec_idorigin_turn_idat_ms
memory_extractions 5 key columnsOne extraction pass over a turn: how many facts it added or invalidated.
conversation_idturn_exec_idextracted_at_msfacts_addedfacts_invalidated
memory_profile_rewrites 2 key columnsOne rewrite of a persona's profile document.
documentupdated_at_ms
memory_summaries 4 key columnsOne private summary of a conversation.
conversation_idsummary_textevent_time_msscope
Admin only 30
These table names are registered only in admin sessions.
summary 3 key columnsOne compaction of the older part of the transcript.
summary_idtextcovers_through_position
approval_details 9 key columnsThe signing-side detail of one approval: caller, approver, edited arguments.
request_reasonrequest_sandbox_modesigner_public_keymodified_args_jsonapproved_for_sessioncallerapproverresponse_sandbox_modeinjected_context
The whole table. A participant session reads the verdict in `approvals`.
attribution_provenance 7 key columnsWho asserted one identity: provider, external id, and the signing edge.
identity_provideridentity_scopeidentity_external_ididentity_display_nameasserting_edge_idsigner_pk_hexsignature_hex
grant_replay_signers 2 key columnsThe key that signed one replayed capability grant.
signer_pk_hexsignature_hex
handoff_signers 2 key columnsThe key that signed one transfer record.
signed_bysigner_key_id
The whole table. `handoffs` carries the verdict, never the key, so a participant session reads `signature_status` and cannot reach this table at all.
trace_signers 4 key columnsThe key that signed one step inside a trace.
ordinalstep_kindrolesigner_key
trace_participant_addresses 3 key columnsThe onchain address on one payment receipt or wallet-link step inside a trace.
ordinalstep_kindaddress
The whole table. A participant's own address exposes its whole balance and history, so `trace_payment_receipts` and `trace_wallet_links` do not carry it.
admin_model_changes 7 key columnsOne signed change of a conversation's model or provider by an administrator.
signature_statusprincipalprevious_providerprevious_modelnew_providernew_modelchanged_at_ms
query_audit_intents 8 key columnsOne query the Query plane recorded before it ran.
namespacequery_idrequestershape_digestrecorded_at_nanossource_pin_countcommand_idcommand_digest
query_audit_completions 11 key columnsThe outcome of one audited query.
namespacequery_idintent_positionoutcomeerror_classduration_nanosrowstruncationtruncated_atcommand_idcommand_digest
query_audit_source_pins 16 key columnsThe exact projection generation and evidence one audited query read.
namespacequery_idpin_indexpin_kindfamilysource_partitionpin_source_incarnationprojection_generationevidence_kindevidence_positionevidence_journal_positionschema_versionfact_versionartifact_digestanchor_headrevision
credential_lifecycle 14 key columnsOne revision of a query credential: capabilities, namespaces, revocation.
namespacerevisionchange_kindentry_digestcredential_idprincipalcap_edgecap_administratorcap_attest_emailcap_may_assert_directcap_forensics_readallowed_namespacesrevokedrevoked_at_ms
credential_key_lifecycle 9 key columnsOne key's activation, expiry, and confirmation inside a credential.
namespacecredential_idkey_idstateactivated_at_mshas_not_afternot_after_mshas_confirmed_atconfirmed_at_ms
credential_lifecycle_refusals 3 key columnsOne refused credential change, and the refusal reason.
namespacechange_kindreason
routine_lifecycle 12 key columnsOne lifecycle event of a routine: published, paused, resumed, removed.
phaseroutineactor_personaconversation_idat_mshas_reasonreasonhas_scopescopehas_channelchannelsigner_public_key
routine_setup 1 key columnsThe routine uid one setup pass registered.
routine_uid
fires 9 key columnsOne firing of a routine: scheduled time, actual time, outcome.
routineroutine_uidoccurrencescheduled_at_msfired_at_mshas_outcomeoutcomehas_grant_driftgrant_drift_tools
The whole table is fleet-only; a participant session cannot name it.
observed_routines 40 key columnsOne observed state of a routine, as the watcher recorded it.
namespaceobservation_ordinalobservation_resource_versionobserved_at_msrecorded_at_msobservernameuidresource_versiongenerationfire_conversation_idcreator_personaprovenance_conversation_idscopedisplay_namedescriptionschedule_jsonschedule_timezonenext_fires_jsonpromptprompt_truncatedreadyhas_phasephasehas_messagemessagehas_last_fire_time_mslast_fire_time_mshas_next_fire_time_msnext_fire_time_msconditions_jsonsuspendedhas_paused_bypaused_byhas_paused_at_mspaused_at_mshas_pause_reasonpause_reasonorphanedsetup_completed
persona_profiles 25 key columnsOne revision of a persona's profile record.
namespaceentry_digestpersona_iddisplay_namestatuscreated_at_msmerged_intomerged_bymerge_evidencemerged_at_msremoved_byremoved_at_mssplit_fromsplit_bysplit_reasonsplit_at_msprofile_languageprofile_toneprofile_updated_at_msprofile_preferencestool_grantsapproval_policyturn_quotaauto_reviewidentities
persona_identities 15 key columnsOne linked identity of a persona, with its link evidence.
namespaceentry_digestdeletedpersona_idsurfacescopesubjectdisplay_nametime_zonefirst_seen_mslast_seen_mslink_methodlinked_bylink_evidenceverified_at_ms
persona_participations 9 key columnsOne persona's participation in one scope.
namespaceentry_digestdeletedpersona_idscoperolefirst_at_mshas_via_personavia_persona
persona_visibility 10 key columnsOne visibility setting a persona holds in a scope.
namespaceentry_digestdeletedpersona_idscopehiddenset_at_msset_byhas_via_personavia_persona
persona_refusals 3 key columnsOne refused persona-directory change.
namespacerecord_kindreason
persona_profiles_current 25 key columnsThe current profile of a persona, after merge and removal folds.
namespaceentry_digestpersona_iddisplay_namestatuscreated_at_msmerged_intomerged_bymerge_evidencemerged_at_msremoved_byremoved_at_mssplit_fromsplit_bysplit_reasonsplit_at_msprofile_languageprofile_toneprofile_updated_at_msprofile_preferencestool_grantsapproval_policyturn_quotaauto_reviewidentities
persona_identities_current 15 key columnsThe current identity set of a persona.
namespaceentry_digestdeletedpersona_idsurfacescopesubjectdisplay_nametime_zonefirst_seen_mslast_seen_mslink_methodlinked_bylink_evidenceverified_at_ms
persona_participations_current 9 key columnsThe current participation set of a persona.
namespaceentry_digestdeletedpersona_idscoperolefirst_at_mshas_via_personavia_persona
memory_provenance_identity 6 key columnsThe identity assertion attached to one memory record.
record_kindrecord_ididentity_provideridentity_scopeidentity_external_ididentity_display_name
memory_fences 0 key columnsOne extraction fence marker: where memory work stands in a partition.
memory_unknown 1 key columnsOne record the memory fold could not classify.
kind
settlements 5 key columnsOne outbound payment attempt and whether a verified receipt settled it.
subjectoutcomeamount_base_unitsassettool_call_id
Only committed turns
The control plane records dispatch intent through the state plane before calling the harness. It records the commit marker after the harness returns. A crash between these writes can leave records for an unfinished turn, including one that caused an external effect.
A turn-derived table gains a row only when its conversation and turn have turn_start and turn_complete markers. An unfinished turn's events are folded into nothing, so no realm can read them — there is no raw-events escape hatch. Tables that are not turn-scoped apply no such gate: refusals keeps the record a blocked attempt must leave, and wallet_link_lifecycle ceremonies run outside any turn.
The filter matches both conversation and turn because clients supply turn ids. A committed turn in one conversation cannot make another conversation's turn eligible.
Realm and scope apply before query planning
The credential you present fixes the realm: a participant grant names only the conversation tables, a fleet grant names every table. A name outside the realm fails to resolve before execution. The scope inside the grant then admits only the partitions your persona participates in.
| Queryable data | In a conversation | Your own sessions | Admin |
|---|---|---|---|
| This conversation's committed turns | Reads | Reads | Reads |
| Message text and tool arguments | Reads | Reads | Reads |
| Every conversation you take part in | Hidden | Reads | Reads |
| Someone else's conversation | Hidden | Hidden | Reads |
| Messages never sent to a client | Hidden | Hidden | Reads |
| Raw signer keys and edge identities | Hidden | Hidden | Reads |
| Undecoded event bytes (events_raw) | Hidden | Hidden | Reads |
| Every persona's profile and wallet | Hidden | Hidden | Reads |
Your scope limits which conversations, tables, and columns you can query. A query fails if it names a table or column that your session's catalog does not register.
The verified caller determines the credential. An admin session mints a short-lived fleet-wide grant. A persona session mints a grant over the conversations that person's participation records name.
Tools running within a conversation use its trusted dispatch attribution to determine scope. Their arguments cannot select another conversation.
Participant grants expose the content already available to participants through transcripts and approval interfaces. These categories live in fleet-only tables:
- Raw signer keys.
- Identity provenance — who asserted each participant identity on the edge.
- Rows a turn marks as never sent to a client.
- Settlement verdicts and routine internals.
- Deployment-wide persona, credential, and query-audit records.
Queries include full tool arguments and results already available to participants, without display truncation.
How an agent reads its own conversation
Two conversation-history tools use fixed SQL statements and do not require approval. conversation_recent_turns lists committed turns, newest first: 20 by default, at most 50. conversation_list_tool_calls lists recorded calls and argument previews: 20 by default, at most 100. Full-detail mode adds turn-failure information or human-approval status, respectively.
Agents cannot submit raw SQL from within a conversation. Model-authored SQL can combine rows with different trust levels, preventing the result from retaining each row's provenance verdict.
Outside conversations, ad-hoc SQL runs against the standalone Query plane. Your session mints a caller-held grant — POST /api/query/grant for your own conversations, POST /api/query/admin-grant for an admin — and the caller presents it as the bearer on a direct Execute call to the Query edge socket. No Control endpoint accepts SQL; Control only mints the credential.
curl -sS -X POST https://<control-plane>/api/query/grant \
-H "authorization: Bearer $POLYCHROME_SESSION"
# → {"token":"…","expires_at_ms":…}
# The grant rides as the bearer on a Connect Execute call to the Query
# edge socket, which streams result frames back:
# POST https://<query-edge>/polychrome.query.v1.QueryService/Execute
# authorization: Bearer <token>
# {"sql":"SELECT model, COUNT(*) AS turns FROM model_call GROUP BY model"}Check truncated before interpreting a result. A true value means the response reached its row limit. Narrow the query or use COUNT(*) to obtain a total.
Query limits
The Query plane parses each statement and checks its type, not its prefix. It accepts exactly one statement, and that statement has to be a query. Data definition, data modification, SHOW, COPY, SET, transaction control, and multi-statement batches are refused before planning. EXPLAIN ANALYZE runs the query, so it is refused everywhere. Plain EXPLAIN is admin-only. Ad hoc external-table references are off for every scope.
| Limit | Default | When the limit is reached |
|---|---|---|
| Wall clock | 30 seconds | The query returns a timeout rather than holding a request open. |
| Rows returned | 10,000 | The result is cut to the cap and flagged with truncated. |
| Concurrent executions | Deployment-configured | Executions beyond the bound wait on a permit; the wall-clock timeout still applies once one starts. |
| Execution memory | Deployment-configured | Queries share one fair pool, and larger queries spill intermediate data to disk under a quota. |
| Grant mints, per caller | 30 a minute | Mints exceeding the budget are refused without queueing. A grant is cheap to re-mint, so retry shortly. |
Each query records an intent before execution and an outcome afterward in the fleet-only query_audit_* tables. If recording the intent fails, the query does not run. An intent without an outcome indicates that no result was recorded. Fixed-statement conversation tools follow the same audit requirement.
Where the rows come from
The projector replays each journal partition once per generation and writes exact, signed artifacts. A query registers only the artifacts its realm and scope admit, then reads them as ordinary columnar tables — statistics and projection pushdown apply. There is no query-time replay and no decode cache; a new generation republishes an artifact rather than mutating one.
A LIMIT can bound a simple plan. Sorting, grouping, and joins build intermediate results before applying the output limit. Narrowing the conversations your scope admits reduces the artifact set a query opens.
The dashboard and the fixed-statement conversation tools run per-request statements against the fleet realm. No maintained dashboard table exists — the totals come out of the same published artifacts.
The query fact model defines the responsibilities of decoding, shared folds, and consumer projections. It also documents table classes, scope, and redaction rules.