Contents
Getting started
Using Codepanion
Connecting your tools
Codepanion reads your source code and queries your production database. A security review is going to ask what was accessed, by whom, and when. The audit log is the answer to that question: an append-only record kept per account, separate from the investigations themselves, written as the work happens rather than reconstructed afterwards.
What a row records
Every entry has the same five parts.
- When. A UTC timestamp, taken at the moment of the action.
- Who. The actor. A person's email address for anything a signed-in user did, including a Codepanion employee. The literal
agentfor a tool call.ingestfor anything a CI push did,connectorfor the on-prem connector, and asystem:prefix for the scheduled jobs. - What. The action identifier, listed in full below.
- Which investigation. Present on anything that happened inside one, so an agent's whole run through your systems can be pulled out as a unit.
- Details. A JSON object whose shape depends on the action: the query text, the environment, the codebase, the result count, the reason a credential was refused.
Rows are scoped to your account at the data layer, by a query filter applied to every table by convention that fails closed. A query for your trail cannot reach another account's rows even if the code asking for them is wrong.
What gets a row
This is the complete list of what can appear in your trail. The identifiers are the values you will see in an export, so they are given verbatim.
Every tool the agent calls
Actor: agent
One row per call, written whether the call succeeded or failed. There is no tool the agent can reach that skips this.
search_codeVector search over your indexed code. Records the query, how many results were asked for, the snapshots searched, the codebase and environment, and how many results came back.search_textKeyword search over your code. Same detail, plus how many matches were file paths.query_databaseA read-only query against a connected database. Records the query definition and the environment it ran against.describe_schemaReading the shape of a connected database: tables, columns, keys.search_logsA search of a connected log source.get_log_contextReading the entries surrounding one moment in your logs.search_docsA search of a connected docs source. Records the query and the provider.read_doc_pageFetching one page from that docs source.search_issuesA read of your issue tracker, usually to match your house style before drafting.create_issueFiling an issue. Approved by a person first.propose_changeTurning a fix into a patch, and whether a draft pull request was opened.run_jsA calculation in your browser sandbox. Records the outcome, the length of the snippet, and its opening.render_chartA chart drawn in your browser from rows already in the sandbox. Records the outcome, the dataset it drew from, the chart type and title, and how many categories and series it holds.read_codepanionThe agent reading one of the .codepanion context files committed to your repository. Records the path and the snapshot it came from.
Approval decisions
Actor: the person who decided, or system when nobody did
The two tools that write somewhere outside Codepanion stop and wait. Both outcomes are recorded, against the investigation they belong to, naming the person who clicked rather than the person who asked the question.
approve_writeA person approved the pending action. Records which tool and which investigation.decline_writeA person declined it, or the card expired. An expiry is recorded with the actor system, because nobody decided.
Saved reports
Actor: a member or an owner
A saved report is a query and a chart your team can run again. Each run goes through the same read-only path as query_database.
create_reportA report was saved. Records its name, environment, query and chart.update_reportA report was renamed or its chart changed. Records the before and after.delete_reportA report was deleted.run_reportA person ran a report. Records the report, the environment and the query it sent, the same way a query_database row does.
Investigation state
Actor: the signed-in user
Changes to an investigation's status, owner, or pinned environment.
close_ticketAn investigation was closed.reopen_ticketAn investigation was reopened.assign_ticketAn investigation was assigned to someone.set_ticket_environmentAn investigation was pinned to an environment.
Credentials and connections
Actor: an owner, or ingest
Anything that changes what Codepanion can reach. Failed authentication is recorded as loudly as successful authentication.
create_ingest_tokenAn API key was minted. Records the label, the scope, and any expiry.revoke_ingest_tokenAn API key was revoked.rotate_ingest_tokenAn API key was rotated by Codepanion staff on request.ingest_authA code push authenticated. Records which key, or for a keyless push, which repository.ingest_auth_failedA code push was refused, with the reason. This is how a leaked or stale key shows itself, and how a repository probing the keyless allowlist shows itself.set_ingest_reposThe keyless CI allowlist was changed.set_ingest_limitA push size or rate limit was changed.connector_authThe on-prem connector connected, after silence.connector_auth_failedA connector was refused.configure_databaseA database connection was added or replaced.remove_databaseA database connection was removed.configure_log_sourceA log source was added or replaced.remove_log_sourceA log source was removed.integration_configuredAn issue tracker or docs source was connected. Records the host the credential is sent to, and the host it replaced.integration_test_failedA connection attempt failed its check, so nothing was saved. Records the host that was tried.integration_testedA connection was tested without saving, and whether it passed. Records the host that was tried.integration_removedAn issue tracker or docs source was disconnected and its stored credential destroyed. Records the host it pointed at.
Codebases, environments, and known issues
Actor: an owner, a member, or ingest
A codebase or environment created by a CI push is recorded exactly like one created in the app, with the actor set to ingest.
create_codebaseA codebase was created, and whether it came from the app or from a push.rename_codebaseA codebase's display name changed. The slug never does.create_environmentAn environment label was seen or created for the first time.rename_environmentAn environment's display name changed.create_pinned_contextA known issue was pinned.delete_pinned_contextA known issue was removed.
Your team
Actor: an owner, the user themselves, or the system
Who is in the account, what they can do, and how they are emailed.
invite_customer_userSomeone was invited, and with which role.resend_customer_inviteAn invitation was sent again.update_customer_userA role changed.set_customer_user_display_nameA user set their own display name.enable_customer_userA disabled account was restored.disable_customer_userAn account was disabled. Also written by the inactivity sweep, with the reason and the last active date.delete_customer_userAn invitation that was never accepted was withdrawn.link_customer_userA first sign-in claimed an invitation. Records the identity it is now bound to, permanently.link_customer_user_failedA sign-in tried to claim an invitation and was refused, with the reason: the invitation was no longer open, or the same address is invited in more than one account.promote_customer_userA sign-in promoted someone to owner because the account had no active owner left.set_notification_prefsA user changed which emails they receive.
The account itself
Actor: Codepanion staff, or the system
Account-level events, including the ones we perform on your behalf.
rename_tenantYour account was renamed.set_credit_settingsYour spending limits were changed.credit_topupCredit was added to your account.suspend_tenantYour account was suspended by Codepanion staff, so every sign-in and every push is refused until it is resumed.resume_tenantA suspended account was resumed.retention_purgeThe nightly retention sweep retired old code snapshots, aged out old agent access records, and deleted investigation transcripts past your retention window. Records exactly what it deleted.tenant_deletedYour account and its data were deleted. Records the counts of everything removed.
Reading an export
Four identifiers still spell an investigation ticket. That is the internal name for the same object, kept stable so a filter you write today keeps working. The app, these docs and everything you will show an auditor call it an investigation.
When we look at your data
The most useful thing in the log is the part about us. Codepanion staff have an internal console for support and billing. Every read of your account through it writes a row into your trail, naming the employee who did it.
staff_view_tenantAn employee opened your account's page in the console.staff_impersonateAn employee opened your account in support mode.staff_view_snapshotsAn employee listed your code pushes.staff_view_usersAn employee listed or opened your team.staff_view_radarAn employee viewed which files the agent has been visiting for you.staff_view_creditsAn employee viewed your credit balance.
The staff actions that change something (rotate_ingest_token, set_credit_settings, credit_topup, rename_tenant, suspend_tenant, resume_tenant, tenant_deleted) carry the employee's address too. There is no house account that anonymises who did what.
Two internal screens span every account at once: the tenant list and the aggregate usage report. Those write a row (staff_list_tenants, staff_view_usage) that belongs to no single account, so they do not appear in your trail. Neither one opens your code, your database, your logs, or your investigations.
What the log does not hold
A record of access is worth less if it becomes a second copy of the thing it is protecting. The log deliberately stops short.
- No result rows. A
query_databaseentry holds the query and the environment. The rows that came back are never copied into it. - No file contents. A search entry holds the query and how many results matched; the code that matched stays out of it.
- No credentials. Connection strings live in Key Vault. A
configure_databaseentry records that an environment was configured, never what it was configured with. - No full sandbox script. A
run_jsentry keeps the outcome, the length, and the first 256 characters. Model output can be arbitrarily long and the audit table is the wrong place to store it. - No conversation. Your messages and the agent's answers live in the investigation and never in the audit log. Opening an investigation, sending a message, and exporting a transcript are not audited events.
For the moment-by-moment version of what the agent did, read the tool steps in the investigation, which show the exact input and output of every call. The audit log is the durable index over that activity.
Who can read it
Your trail is yours, and it is complete: the agent's calls, your team's actions, and our staff's access all sit in one stream in one order.
Be aware of where it is surfaced today. There is no audit log screen in the Codepanion app. Reading it is an API call, authenticated with the same sign-in the app uses, so it is reachable programmatically rather than by clicking through the product. Reading is not gated by role: any account in your team, viewer included, is permitted, because reviewing access is not a privileged act. An API key cannot read it. An ingest-scoped key can only push code, a connector-scoped key can only open the connector channel, and neither limit is relaxed for this.
In practice, if you want your trail for a review, ask us and we will export it. A screen in the app is planned. This page describes what is recorded, which is true today regardless of where you read it from.
Getting a copy
An export is a single request, filtered by action and by date range, and it returns the whole filtered set rather than a page of it. CSV and JSON are both available, ordered oldest first so the file reads chronologically. One request returns at most 50,000 rows. That ceiling is not announced in the file, so narrow a large export by date range and check the row count against what you expected.
auditId,createdAt,actor,action,ticketId,details
1042,2026-08-03T09:12:04.117Z,agent,search_code,58,"{""query"":""checkout retry"",""k"":8,""codebase"":""billing"",""environment"":""prod"",""resultCount"":8}"
1043,2026-08-03T09:12:31.882Z,agent,query_database,58,"{""query"":{...},""environment"":""prod""}"
1044,2026-08-03T09:13:02.006Z,sam@acme.example,approve_write,58,"{""tool"":""create_issue"",""approved"":true}"
1051,2026-08-03T11:40:19.554Z,dana@codepanion.app,staff_impersonate,,"{}"The ticketId column is an internal correlation identifier. Rows sharing a value belong to one investigation, which is what a reviewer needs from it. It is not the investigation number shown in the app, so do not expect the two to match.
The columns are stable, which makes the CSV suitable for loading into a SIEM on a schedule and the JSON suitable for attaching to a SOC 2 evidence request.
Integrity and retention
In production the audit table is an append-only ledger table in Azure SQL. The database engine itself refuses to update or delete a row, so the application accounts cannot rewrite history even though they own the database. Entries are not covered by the nightly retention sweep, which retires old code snapshots, access records and aged transcripts and leaves the trail alone, and they survive account deletion as a record of processing after the code, embeddings, conversations and stored credentials are gone.