From 001746a3348280a5aeac78b0cf57f3566f225627 Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Mon, 7 Sep 2026 11:34:23 -0400 Subject: [PATCH 1/9] docs: add the audit log page Covers what the audit log records, retention by plan, who can read it, and how to page it over the REST API. Every action name, retention value, and query parameter is taken from the implementation rather than the design. Co-Authored-By: Claude Opus 5 (1M context) --- docs.json | 1 + other-topics/audit-log.mdx | 103 +++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 other-topics/audit-log.mdx diff --git a/docs.json b/docs.json index e2ab4c22..c0e2f15a 100644 --- a/docs.json +++ b/docs.json @@ -106,6 +106,7 @@ "security", "other-topics/using-secrets", "other-topics/request-response-logging", + "other-topics/audit-log", "calculating-cost" ] } diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx new file mode 100644 index 00000000..6f5c1a51 --- /dev/null +++ b/other-topics/audit-log.mdx @@ -0,0 +1,103 @@ +--- +title: "Audit Log" +description: Review who changed what in a Cerebrium project — app, secret, credential, member, and volume actions, with retention and API access by plan. +--- + +The audit log records control-plane actions taken against a project: who took the action, what it acted on, when, and whether it succeeded. Use it to answer who deleted an app, who read a secret, and who invited a member. + +Inference requests are not audited. App invocations are traffic rather than configuration changes, and appear in [app logs](/other-topics/request-response-logging) instead. + +## Availability + +The audit log is included on the Standard and Enterprise plans. Retention sets how far back the log is readable: + +| Plan | Retention | +| ---------- | ----------- | +| Hobby | Not included | +| Standard | 7 days | +| Enterprise | 30 days | + + + Actions are recorded on every plan, including Hobby. Retention governs how far + back the log can be read, not what is captured — upgrading a plan reveals + history that was already recorded rather than starting an empty log. + + +## Viewing the Log + +Open a project in the [dashboard](https://dashboard.cerebrium.ai/login) and select **Audit Log**. Filter by date range, action, or outcome. Select a row to expand the full detail of the entry. + +Only project owners can read the audit log. It lists credential and membership activity across the whole project, so members and service accounts are denied. + +## Recorded Actions + +| Action | Records | +| ------ | ------- | +| `app.create` | An app was created | +| `app.update` | An app's configuration was changed | +| `app.delete` | An app was deleted | +| `build.download` | An app's source was downloaded from a build | +| `build.cancel` | A build was cancelled | +| `container.stop` | A running container was stopped | +| `run.cancel` | An async run was cancelled | +| `secrets.read` | Secret values were retrieved, at project or app level | +| `secrets.update` | Secrets were changed, at project or app level | +| `apikey.create` | An API key was created | +| `apikey.read` | API keys were listed, which returns their values | +| `apikey.delete` | An API key was deleted | +| `serviceaccount.create` | A service account was created | +| `serviceaccount.update` | A service account was changed | +| `serviceaccount.delete` | A service account was deleted | +| `serviceaccount.keys_list` | A service account's tokens were listed | +| `project.member_invite` | A user was invited to the project | +| `project.member_remove` | A user was removed from the project | +| `volume.file_download` | A file was downloaded from a volume | +| `volume.file_delete` | A file was deleted from a volume | +| `volume.resize` | A volume was resized | +| `project.delete` | The project was deleted | + +Reads are recorded where the read itself is sensitive. `secrets.read`, `apikey.read`, and `build.download` return secret values, key values, and source code respectively, so each earns an entry. + +## Entry Contents + +Each entry records: + +- **Time** — when the action was taken +- **Action** — the action name from the table above +- **Actor** — the user or service account that took the action, with the email address, IP address, and user agent of the request +- **Target** — what was acted on, as a type and ID, such as `app:my-app` +- **Outcome** — `success` or `failure`, with the HTTP status code +- **Details** — action-specific fields, such as the invited email address and role on `project.member_invite`, or the file path and region on `volume.file_delete` + +Failed attempts are recorded alongside successful ones. An action denied for lack of permission is itself audit material, so a `failure` entry with a `403` status is expected rather than an error. + +Actors are stored as IDs and resolved to names when the log is read, so an entry stays attributable after a rename. An actor whose account has since been deleted shows as `Deleted user`. + +## Reading the Log Through the API + +The log is also available over the REST API, authenticated as a project owner: + +```bash +curl -X GET "https://rest.cerebrium.ai/v2/projects/{project_id}/audit-log?limit=50" \ + -H "Authorization: Bearer " +``` + +Supported query parameters: + +| Parameter | Description | +| --------- | ----------- | +| `from` | Start of the window, RFC3339. Clamped to the plan's retention window | +| `to` | End of the window, RFC3339. Defaults to now | +| `action` | Return only this action, for example `secrets.read` | +| `actor` | Return only actions taken by this actor ID | +| `outcome` | Return only `success` or `failure` | +| `limit` | Page size, 1–200. Defaults to 50 | +| `nextToken` | Page token from the previous response | + +Entries are returned newest first. When `hasMore` is true, pass the response's `nextPageToken` as `nextToken` to fetch the next page. There is no total count — the log reports what it returned and whether more remains. + + + A `from` earlier than the plan's retention window is clamped to that window + rather than rejected. A window that falls entirely outside it returns an empty + page. + From de4edc1e3e0e1800238f7b3b100cc2ea6c46a472 Mon Sep 17 00:00:00 2001 From: jonoirwinrsa Date: Mon, 7 Sep 2026 15:34:35 +0000 Subject: [PATCH 2/9] Prettified Code! --- other-topics/audit-log.mdx | 74 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index 6f5c1a51..d37d8ba2 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -11,11 +11,11 @@ Inference requests are not audited. App invocations are traffic rather than conf The audit log is included on the Standard and Enterprise plans. Retention sets how far back the log is readable: -| Plan | Retention | -| ---------- | ----------- | +| Plan | Retention | +| ---------- | ------------ | | Hobby | Not included | -| Standard | 7 days | -| Enterprise | 30 days | +| Standard | 7 days | +| Enterprise | 30 days | Actions are recorded on every plan, including Hobby. Retention governs how far @@ -31,30 +31,30 @@ Only project owners can read the audit log. It lists credential and membership a ## Recorded Actions -| Action | Records | -| ------ | ------- | -| `app.create` | An app was created | -| `app.update` | An app's configuration was changed | -| `app.delete` | An app was deleted | -| `build.download` | An app's source was downloaded from a build | -| `build.cancel` | A build was cancelled | -| `container.stop` | A running container was stopped | -| `run.cancel` | An async run was cancelled | -| `secrets.read` | Secret values were retrieved, at project or app level | -| `secrets.update` | Secrets were changed, at project or app level | -| `apikey.create` | An API key was created | -| `apikey.read` | API keys were listed, which returns their values | -| `apikey.delete` | An API key was deleted | -| `serviceaccount.create` | A service account was created | -| `serviceaccount.update` | A service account was changed | -| `serviceaccount.delete` | A service account was deleted | -| `serviceaccount.keys_list` | A service account's tokens were listed | -| `project.member_invite` | A user was invited to the project | -| `project.member_remove` | A user was removed from the project | -| `volume.file_download` | A file was downloaded from a volume | -| `volume.file_delete` | A file was deleted from a volume | -| `volume.resize` | A volume was resized | -| `project.delete` | The project was deleted | +| Action | Records | +| -------------------------- | ----------------------------------------------------- | +| `app.create` | An app was created | +| `app.update` | An app's configuration was changed | +| `app.delete` | An app was deleted | +| `build.download` | An app's source was downloaded from a build | +| `build.cancel` | A build was cancelled | +| `container.stop` | A running container was stopped | +| `run.cancel` | An async run was cancelled | +| `secrets.read` | Secret values were retrieved, at project or app level | +| `secrets.update` | Secrets were changed, at project or app level | +| `apikey.create` | An API key was created | +| `apikey.read` | API keys were listed, which returns their values | +| `apikey.delete` | An API key was deleted | +| `serviceaccount.create` | A service account was created | +| `serviceaccount.update` | A service account was changed | +| `serviceaccount.delete` | A service account was deleted | +| `serviceaccount.keys_list` | A service account's tokens were listed | +| `project.member_invite` | A user was invited to the project | +| `project.member_remove` | A user was removed from the project | +| `volume.file_download` | A file was downloaded from a volume | +| `volume.file_delete` | A file was deleted from a volume | +| `volume.resize` | A volume was resized | +| `project.delete` | The project was deleted | Reads are recorded where the read itself is sensitive. `secrets.read`, `apikey.read`, and `build.download` return secret values, key values, and source code respectively, so each earns an entry. @@ -84,15 +84,15 @@ curl -X GET "https://rest.cerebrium.ai/v2/projects/{project_id}/audit-log?limit= Supported query parameters: -| Parameter | Description | -| --------- | ----------- | -| `from` | Start of the window, RFC3339. Clamped to the plan's retention window | -| `to` | End of the window, RFC3339. Defaults to now | -| `action` | Return only this action, for example `secrets.read` | -| `actor` | Return only actions taken by this actor ID | -| `outcome` | Return only `success` or `failure` | -| `limit` | Page size, 1–200. Defaults to 50 | -| `nextToken` | Page token from the previous response | +| Parameter | Description | +| ----------- | -------------------------------------------------------------------- | +| `from` | Start of the window, RFC3339. Clamped to the plan's retention window | +| `to` | End of the window, RFC3339. Defaults to now | +| `action` | Return only this action, for example `secrets.read` | +| `actor` | Return only actions taken by this actor ID | +| `outcome` | Return only `success` or `failure` | +| `limit` | Page size, 1–200. Defaults to 50 | +| `nextToken` | Page token from the previous response | Entries are returned newest first. When `hasMore` is true, pass the response's `nextPageToken` as `nextToken` to fetch the next page. There is no total count — the log reports what it returned and whether more remains. From 3e72f3b8f5ca5868764cee6ea0372ed61b104dca Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Mon, 7 Sep 2026 16:16:49 -0400 Subject: [PATCH 3/9] docs: drop the retention note, add a filtering section The note told Hobby readers their activity is recorded where they cannot see it, which reads as surveillance rather than as a reason to upgrade. The retention table already says what each plan gets. Replace the passing mention of the filters with a short section framed around the questions people actually open the log to answer. Co-Authored-By: Claude Opus 5 (1M context) --- other-topics/audit-log.mdx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index d37d8ba2..a0f7c40e 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -17,18 +17,22 @@ The audit log is included on the Standard and Enterprise plans. Retention sets h | Standard | 7 days | | Enterprise | 30 days | - - Actions are recorded on every plan, including Hobby. Retention governs how far - back the log can be read, not what is captured — upgrading a plan reveals - history that was already recorded rather than starting an empty log. - - ## Viewing the Log -Open a project in the [dashboard](https://dashboard.cerebrium.ai/login) and select **Audit Log**. Filter by date range, action, or outcome. Select a row to expand the full detail of the entry. +Open a project in the [dashboard](https://dashboard.cerebrium.ai/login) and select **Audit Log**. Select a row to expand the full detail of the entry. Only project owners can read the audit log. It lists credential and membership activity across the whole project, so members and service accounts are denied. +## Filtering + +Filter by date range, action, or outcome to answer a specific question: + +- **Who removed someone from the project last week** — action `project.member_remove`, over that week +- **What was refused** — outcome `failure`, which lists the attempts that were denied +- **Whether a secret was read before an incident** — action `secrets.read`, over the hours before it + +Filtering by actor is available over the API rather than in the dashboard. + ## Recorded Actions | Action | Records | From 4603106e67dfe988ddee6628edb5b89fc6de7c89 Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Mon, 7 Sep 2026 16:17:29 -0400 Subject: [PATCH 4/9] docs: cut the explanation of who is denied Saying which roles are refused invites the reader to reason about access they do not have. The line above already says who can read it. Co-Authored-By: Claude Opus 5 (1M context) --- other-topics/audit-log.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index a0f7c40e..62a309ba 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -21,7 +21,7 @@ The audit log is included on the Standard and Enterprise plans. Retention sets h Open a project in the [dashboard](https://dashboard.cerebrium.ai/login) and select **Audit Log**. Select a row to expand the full detail of the entry. -Only project owners can read the audit log. It lists credential and membership activity across the whole project, so members and service accounts are denied. +Only project owners can read the audit log. ## Filtering From 6836706d061e6d2aa43972595b1205e1f186e2a6 Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Fri, 11 Sep 2026 12:32:25 -0400 Subject: [PATCH 5/9] docs: drop the actor storage detail from the audit log page How actors are persisted and resolved is ours, not the reader's. Keep the behaviour a rename does not break attribution and drop the mechanism. Co-Authored-By: Claude Opus 5 (1M context) --- other-topics/audit-log.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index 62a309ba..5b05fef9 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -75,7 +75,7 @@ Each entry records: Failed attempts are recorded alongside successful ones. An action denied for lack of permission is itself audit material, so a `failure` entry with a `403` status is expected rather than an error. -Actors are stored as IDs and resolved to names when the log is read, so an entry stays attributable after a rename. An actor whose account has since been deleted shows as `Deleted user`. +An entry stays attributable after a user is renamed. An actor whose account has since been deleted shows as `Deleted user`. ## Reading the Log Through the API From 9fdba8d54358c3858f90b6bf22e2584c945325ad Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Fri, 11 Sep 2026 12:35:03 -0400 Subject: [PATCH 6/9] docs: stop saying what the sensitive reads return The action table only needs to name the action. What each read hands back is not the reader's business. Co-Authored-By: Claude Opus 5 (1M context) --- other-topics/audit-log.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index 5b05fef9..fa55657e 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -47,7 +47,7 @@ Filtering by actor is available over the API rather than in the dashboard. | `secrets.read` | Secret values were retrieved, at project or app level | | `secrets.update` | Secrets were changed, at project or app level | | `apikey.create` | An API key was created | -| `apikey.read` | API keys were listed, which returns their values | +| `apikey.read` | API keys were listed | | `apikey.delete` | An API key was deleted | | `serviceaccount.create` | A service account was created | | `serviceaccount.update` | A service account was changed | @@ -60,7 +60,7 @@ Filtering by actor is available over the API rather than in the dashboard. | `volume.resize` | A volume was resized | | `project.delete` | The project was deleted | -Reads are recorded where the read itself is sensitive. `secrets.read`, `apikey.read`, and `build.download` return secret values, key values, and source code respectively, so each earns an entry. +Reads are recorded where the read itself is sensitive, which is why `secrets.read`, `apikey.read`, and `build.download` each have an entry. ## Entry Contents From 189c6600e380a786466d1eed65641fa859a02d49 Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Fri, 11 Sep 2026 12:35:22 -0400 Subject: [PATCH 7/9] docs: cut the actor attribution paragraph Rename and deleted-account behaviour is edge-case trivia nobody reads the page for. Co-Authored-By: Claude Opus 5 (1M context) --- other-topics/audit-log.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index fa55657e..0eb25959 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -75,8 +75,6 @@ Each entry records: Failed attempts are recorded alongside successful ones. An action denied for lack of permission is itself audit material, so a `failure` entry with a `403` status is expected rather than an error. -An entry stays attributable after a user is renamed. An actor whose account has since been deleted shows as `Deleted user`. - ## Reading the Log Through the API The log is also available over the REST API, authenticated as a project owner: From f1a7d3fa4551ca903a492859a4b36f5262d23a11 Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Fri, 11 Sep 2026 12:36:39 -0400 Subject: [PATCH 8/9] docs: drop the note about actor filtering being API-only No reason to document where the dashboard falls short. The actor parameter is already in the API table for anyone who needs it. Co-Authored-By: Claude Opus 5 (1M context) --- other-topics/audit-log.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index 0eb25959..5defb287 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -31,8 +31,6 @@ Filter by date range, action, or outcome to answer a specific question: - **What was refused** — outcome `failure`, which lists the attempts that were denied - **Whether a secret was read before an incident** — action `secrets.read`, over the hours before it -Filtering by actor is available over the API rather than in the dashboard. - ## Recorded Actions | Action | Records | From e6e01882452aad2af69222bfafd2daf822fc6aba Mon Sep 17 00:00:00 2001 From: Jonathan Irwin Date: Fri, 11 Sep 2026 12:39:02 -0400 Subject: [PATCH 9/9] docs: cut the audit log page back to what a reader needs Drop the rationale for why each action is recorded, the note restating the from clamp already in the table, and the pagination sentence that said nothing. Fold the one-sentence filtering section into viewing. Co-Authored-By: Claude Opus 5 (1M context) --- other-topics/audit-log.mdx | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/other-topics/audit-log.mdx b/other-topics/audit-log.mdx index 5defb287..da5a2224 100644 --- a/other-topics/audit-log.mdx +++ b/other-topics/audit-log.mdx @@ -3,9 +3,9 @@ title: "Audit Log" description: Review who changed what in a Cerebrium project — app, secret, credential, member, and volume actions, with retention and API access by plan. --- -The audit log records control-plane actions taken against a project: who took the action, what it acted on, when, and whether it succeeded. Use it to answer who deleted an app, who read a secret, and who invited a member. +The audit log records actions taken against a project: who took the action, what it acted on, when, and whether it succeeded. Use it to answer who deleted an app, who read a secret, and who invited a member. -Inference requests are not audited. App invocations are traffic rather than configuration changes, and appear in [app logs](/other-topics/request-response-logging) instead. +Inference requests are not audited. They appear in [app logs](/other-topics/request-response-logging) instead. ## Availability @@ -21,15 +21,7 @@ The audit log is included on the Standard and Enterprise plans. Retention sets h Open a project in the [dashboard](https://dashboard.cerebrium.ai/login) and select **Audit Log**. Select a row to expand the full detail of the entry. -Only project owners can read the audit log. - -## Filtering - -Filter by date range, action, or outcome to answer a specific question: - -- **Who removed someone from the project last week** — action `project.member_remove`, over that week -- **What was refused** — outcome `failure`, which lists the attempts that were denied -- **Whether a secret was read before an incident** — action `secrets.read`, over the hours before it +Filter by date range, action, or outcome. Only project owners can read the audit log. ## Recorded Actions @@ -58,8 +50,6 @@ Filter by date range, action, or outcome to answer a specific question: | `volume.resize` | A volume was resized | | `project.delete` | The project was deleted | -Reads are recorded where the read itself is sensitive, which is why `secrets.read`, `apikey.read`, and `build.download` each have an entry. - ## Entry Contents Each entry records: @@ -71,7 +61,7 @@ Each entry records: - **Outcome** — `success` or `failure`, with the HTTP status code - **Details** — action-specific fields, such as the invited email address and role on `project.member_invite`, or the file path and region on `volume.file_delete` -Failed attempts are recorded alongside successful ones. An action denied for lack of permission is itself audit material, so a `failure` entry with a `403` status is expected rather than an error. +Failed attempts are recorded alongside successful ones. ## Reading the Log Through the API @@ -94,10 +84,4 @@ Supported query parameters: | `limit` | Page size, 1–200. Defaults to 50 | | `nextToken` | Page token from the previous response | -Entries are returned newest first. When `hasMore` is true, pass the response's `nextPageToken` as `nextToken` to fetch the next page. There is no total count — the log reports what it returned and whether more remains. - - - A `from` earlier than the plan's retention window is clamped to that window - rather than rejected. A window that falls entirely outside it returns an empty - page. - +Entries are returned newest first. When `hasMore` is true, pass the response's `nextPageToken` as `nextToken` to fetch the next page.