Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ on:
push:
branches: [main]
paths:
- 'docs/**'
- 'docs-site/**'
- 'src/**'
- 'mkdocs.yml'
- 'typedoc.json'
- 'package.json'
- '.github/workflows/mkdocs-deploy.yml'
- '.github/workflows/docs-deploy.yml'
workflow_dispatch:

permissions:
Expand All @@ -21,9 +20,6 @@ concurrency:
group: pages
cancel-in-progress: false

env:
DISABLE_MKDOCS_2_WARNING: 'true'

jobs:
build:
runs-on: ubuntu-latest
Expand All @@ -35,28 +31,26 @@ jobs:
node-version: '20'
cache: npm

- name: Install dependencies
- name: Install SDK dependencies
run: npm ci

- name: Enforce @example coverage
run: npm run docs:check

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install MkDocs
run: pip install mkdocs mkdocs-material mkdocs-literate-nav

- name: Generate API reference (TypeDoc)
run: npm run docs:api

- name: Build docs
run: mkdocs build --strict
- name: Install docs-site dependencies
working-directory: docs-site
run: npm ci

- name: Build docs site
working-directory: docs-site
run: npm run build

- uses: actions/upload-pages-artifact@v3
with:
path: site
path: docs-site/build

deploy:
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@ tsup.config.bundled_*.mjs
.vscode/
.idea/

# TypeDoc generated API reference
docs/reference/api/
# TypeDoc generated API reference (regenerated by `npm run docs:api`)
docs-site/docs/reference/api/
6 changes: 2 additions & 4 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,5 @@ coverage
tsup.config.bundled_*
review-*
specs
# Prose docs use mkdocs-material syntax (admonitions, content tabs) that
# conflicts with prettier's CommonMark formatting; mkdocs owns this dir.
docs/
site
# Docusaurus site has its own formatting/tooling; generated API ref is huge.
docs-site/
20 changes: 20 additions & 0 deletions docs-site/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependencies
/node_modules

# Production
/build

# Generated files
.docusaurus
.cache-loader

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
41 changes: 41 additions & 0 deletions docs-site/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Website

This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.

## Installation

```bash
yarn
```

## Local Development

```bash
yarn start
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

## Build

```bash
yarn build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

## Deployment

Using SSH:

```bash
USE_SSH=true yarn deploy
```

Not using SSH:

```bash
GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ relates to the upstream OpenAPI specs and the Python `pan-aisecurity` SDK, what
change, how `.passthrough()` keeps responses forward-compatible, and the two-stage automated tooling
(`preflight` + `audit:live`) plus the changeset-driven release flow that keep schemas honest.

!!! tip "Looking for the symbol-level docs?"
The TypeDoc-generated [Full API reference](../reference/api/index.md) is the authoritative
listing of every exported type and schema. This page is about the _policy_ around those exports.

:::tip[Looking for the symbol-level docs?]
The TypeDoc-generated [Full API reference](../reference/api/index.md) is the authoritative
listing of every exported type and schema. This page is about the _policy_ around those exports.
:::
## Semantic versioning (pre-1.0)

The SDK is published as `@cdot65/prisma-airs-sdk`. At the time of writing the version in
Expand Down Expand Up @@ -94,12 +94,12 @@ The SDK also tracks the official Python `pan-aisecurity` SDK conceptually — fi
**extends beyond** it: the Python SDK covers scanning, whereas this SDK covers all three service
domains (AI Runtime Security, Model Security, AI Red Teaming) plus configuration/DLP management.

!!! note "Why some DLP specs are excluded"
`specs/dlp/` contains more yaml than the SDK models. The preflight script whitelists only the
four DLP specs that are actually implemented (`DataFilteringProfiles`, `DataPatterns`,
`DataProfiles`, `Dictionaries`). Loading the rest would create component-name collisions (e.g.
two different `Policy` definitions) and produce false drift against unrelated schemas.

:::note[Why some DLP specs are excluded]
`specs/dlp/` contains more yaml than the SDK models. The preflight script whitelists only the
four DLP specs that are actually implemented (`DataFilteringProfiles`, `DataPatterns`,
`DataProfiles`, `Dictionaries`). Loading the rest would create component-name collisions (e.g.
two different `Policy` definitions) and produce false drift against unrelated schemas.
:::
## Preflight: catching Zod-vs-OpenAPI drift in CI

`npm run preflight` (`scripts/preflight-schemas.ts`) is the static drift gate. It:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ This page explains **how** the Prisma AIRS TypeScript SDK works under the hood:
domains it covers, the two authentication strategies, the single request pipeline every client
shares, and the validation and error models that hold it all together.

!!! tip "Looking for the symbol-level docs?"
The TypeDoc-generated [Full API reference](../reference/api/index.md) documents every exported
class, function, type, and Zod schema. This page covers the design _behind_ those symbols.

:::tip[Looking for the symbol-level docs?]
The TypeDoc-generated [Full API reference](../reference/api/index.md) documents every exported
class, function, type, and Zod schema. This page covers the design _behind_ those symbols.
:::
The SDK has **zero external HTTP dependencies** — it is built on the runtime's native `fetch` and
`crypto`, with `zod` as the only production dependency.

Expand Down Expand Up @@ -102,14 +102,14 @@ Walking it explicitly:
5. **Validate.** On success, the body text is read once. If no `responseSchema` was declared,
`request()` returns `undefined`. Otherwise the body is parsed and validated (next section).

!!! note "Empty bodies are hydrated to `{}`"
The AIRS API sometimes returns an empty 2xx body when an endpoint has zero results (e.g.
`/v1/mgmt/scanlogs` with no logs in range). `request()` hydrates an empty body to `{}` before
validation, so all-optional schemas parse cleanly and required-field failures surface on a
specific path rather than a cryptic root error. Endpoints that legitimately return 200+body or
204+no-body (e.g. DLP dictionaries `PUT`) set `allowEmptyBody`, which resolves an empty body to
`undefined` and skips validation entirely.

:::note[Empty bodies are hydrated to `{}`]
The AIRS API sometimes returns an empty 2xx body when an endpoint has zero results (e.g.
`/v1/mgmt/scanlogs` with no logs in range). `request()` hydrates an empty body to `{}` before
validation, so all-optional schemas parse cleanly and required-field failures surface on a
specific path rather than a cryptic root error. Endpoints that legitimately return 200+body or
204+no-body (e.g. DLP dictionaries `PUT`) set `allowEmptyBody`, which resolves an empty body to
`undefined` and skips validation entirely.
:::
## The AuthAdapter abstraction

Authentication is a single plug-point. Both strategies implement the `AuthAdapter` interface from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@ export PANW_MGMT_ENDPOINT=https://api.uk.sase.paloaltonetworks.com/aisec
export PANW_MGMT_ENDPOINT=https://api.gov.sase.paloaltonetworks.com/aisec
```

!!! info "Shared Credentials"
If you use the same OAuth2 client for all services, you only need to set `PANW_MGMT_CLIENT_ID`, `PANW_MGMT_CLIENT_SECRET`, and `PANW_MGMT_TSG_ID`. The Model Security and Red Team clients will use these automatically.
:::info[Shared Credentials]
If you use the same OAuth2 client for all services, you only need to set `PANW_MGMT_CLIENT_ID`, `PANW_MGMT_CLIENT_SECRET`, and `PANW_MGMT_TSG_ID`. The Model Security and Red Team clients will use these automatically.
:::
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ console.log(`API Key: ${newKey.api_key}`); // only returned on create
console.log(`Expires: ${newKey.expiration}`);
```

!!! warning "Save the API key value"
The full `api_key` value is only returned on creation and regeneration. Store it securely (e.g. a secrets manager) immediately — it cannot be retrieved later.

:::warning[Save the API key value]
The full `api_key` value is only returned on creation and regeneration. Store it securely (e.g. a secrets manager) immediately — it cannot be retrieved later.
:::
---

## Identify Keys Needing Rotation
Expand Down Expand Up @@ -126,9 +126,9 @@ for (const key of expiring) {
}
```

!!! tip "Zero-downtime rotation"
To avoid downtime, update your application's secrets store with the new key value **before** the old key expires. The old key remains valid until its original expiration time.

:::tip[Zero-downtime rotation]
To avoid downtime, update your application's secrets store with the new key value **before** the old key expires. The old key remains valid until its original expiration time.
:::
---

## Delete an Old Key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ Two ideas to keep in mind:
- **One client, many sub-clients.** You construct a single `ManagementClient`; each resource hangs off it as a property (`client.profiles`, `client.topics`, …). Auth is shared and managed for you.
- **Everything is tenant-scoped.** All operations run against the Tenant Service Group (TSG) you authenticate with — there's no cross-tenant access.

!!! tip "Typical workflow"
Create a profile (and any custom topics it needs) here → reference that profile by name from a [scan](scan-api.md) → later, query `scanLogs` to see what it caught and refine the profile.

:::tip[Typical workflow]
Create a profile (and any custom topics it needs) here → reference that profile by name from a [scan](scan-api.md) → later, query `scanLogs` to see what it caught and refine the profile.
:::
## Authentication

The Management API uses OAuth2 `client_credentials` flow, separate from the scan API's API key auth. Three values are required:
Expand Down Expand Up @@ -540,18 +540,18 @@ try {

## Get the most out of it

!!! tip "Reuse one client"
Build a single `ManagementClient` and share it across your app. It owns the OAuth token cache — every sub-client (`profiles`, `topics`, …) shares the same token, so you authenticate once and reuse it everywhere. Constructing a fresh client per call throws that cache away.

!!! warning "Delete is referential — expect 409s"
A standard `delete` on a profile or topic that's still referenced by a policy fails with a **409 conflict** (the response lists the referencing policies). That's a safety net, not a bug. Either detach the references first, or call `forceDelete` to remove it anyway. Note the asymmetry:

- `profiles.forceDelete(id, updatedBy)` — `updatedBy` is **required**.
- `topics.forceDelete(id, updatedBy?)` — `updatedBy` is **optional**.

!!! note "List endpoints paginate"
`list()` returns up to 100 items by default. Pass `{ offset, limit }` and follow `next_offset` (or `page_token` for scan logs) until it comes back `undefined` to walk the full set. Don't assume the first page is everything.

:::tip[Reuse one client]
Build a single `ManagementClient` and share it across your app. It owns the OAuth token cache — every sub-client (`profiles`, `topics`, …) shares the same token, so you authenticate once and reuse it everywhere. Constructing a fresh client per call throws that cache away.
:::
:::warning[Delete is referential — expect 409s]
A standard `delete` on a profile or topic that's still referenced by a policy fails with a **409 conflict** (the response lists the referencing policies). That's a safety net, not a bug. Either detach the references first, or call `forceDelete` to remove it anyway. Note the asymmetry:

- `profiles.forceDelete(id, updatedBy)` — `updatedBy` is **required**.
- `topics.forceDelete(id, updatedBy?)` — `updatedBy` is **optional**.
:::
:::note[List endpoints paginate]
`list()` returns up to 100 items by default. Pass `{ offset, limit }` and follow `next_offset` (or `page_token` for scan logs) until it comes back `undefined` to walk the full set. Don't assume the first page is everything.
:::
**Look profiles up by name when you can.** `profiles.getByName('my-profile')` saves you a list-then-filter, and returns the highest revision if several exist. Both `get` and `getByName` throw `AISecSDKException` when nothing matches — handle that rather than expecting `null`.

**Rotate keys, don't recreate them.** `apiKeys.regenerate(id, …)` issues a new secret while preserving the key's identity and rotation policy — cleaner than deleting and re-adding.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,9 @@ console.log(auth.expires_at);

## Get the most out of it

!!! tip "Define groups once, scan many times"
Treat security groups as reusable policy. Create one per source type (Hugging Face, S3, etc.), tune its rule states, and point every scan at it. Editing a single group is far easier than re-specifying rules per scan, and it gives you one place to audit your posture.

:::tip[Define groups once, scan many times]
Treat security groups as reusable policy. Create one per source type (Hugging Face, S3, etc.), tune its rule states, and point every scan at it. Editing a single group is far easier than re-specifying rules per scan, and it gives you one place to audit your posture.
:::
- **Start in `ALLOWING`, then escalate.** When rolling out a new rule, set it to `ALLOWING` first so scans still pass while you observe what it flags. Flip it to `BLOCKING` once you trust the signal. This avoids surprise CI failures.
- **Always inspect violations, not just the outcome.** A `BLOCKED` outcome tells you _that_ something failed; `getViolations()` and `getEvaluations()` tell you _what_ and _why_. `getEvaluations()` groups results by rule; `getViolations()` lists individual findings with descriptions.
- **Scans are asynchronous.** A fresh scan returns `PENDING`. Poll `get()` (with a sensible delay) until the outcome settles — don't assume the create response is the final verdict.
Expand Down
Loading
Loading