Skip to main content

API Design & Versioning

This page describes the design and versioning philosophy of the Prisma AIRS TypeScript SDK: how it relates to the upstream OpenAPI specs and the Python pan-aisecurity SDK, what counts as a breaking change, how .passthrough() keeps responses forward-compatible, and the automated preflight tooling plus the changeset-driven release flow that keep schemas honest.

Looking for the symbol-level docs?

The TypeDoc-generated Full API reference 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 package.json is 0.20.1 — i.e. pre-1.0.

Under pre-1.0 semver, the usual guarantees shift down one level:

  • Minor (0.x.0) releases may contain breaking changes. Type reshapes that change a public response shape land as minor bumps.
  • Patch (0.x.y) releases are bug fixes, internal changes, schema-drift corrections, and additive-only changes that don't alter existing public types.

Once the SDK reaches 1.0.0, normal semver applies and breaking changes move to major bumps. Until then, pin a version and read the release notes / changesets before upgrading.

What counts as a breaking change​

For this SDK, the surface that matters is the TypeScript types consumers compile against, not just runtime behavior. A change is breaking if existing, correct caller code stops compiling or silently changes meaning. Examples drawn from real changesets:

  • Reshaping a response type — e.g. the ScanResponse.tool_detected IODetected / ScanSummary remodel, where flag fields moved from input_detected.<flag> into a detection_entries[] walk.
  • Making a previously-optional field required (or vice versa) on a response type.
  • Removing or renaming an exported symbol, enum member, or method.

By contrast, these are non-breaking: adding a new optional field to a request, adding a new exported schema/type, the server adding a field your .passthrough() schema now carries through, and correcting a schema so it matches what the API already returned.

Forward compatibility via .passthrough()​

Response models in src/models/*.ts are Zod schemas whose object validators end in .passthrough(). (Request schemas are the deliberate exception: AI Gateway and Red Team write bodies use .strict() so misspelled fields fail locally, and a few AI Gateway routing/provider objects use .catchall() for finite-JSON extension points.)

export const ApiKeySchema = z
.object({
api_key_id: z.string(),
api_key_name: z.string().optional(),
// ...
})
.passthrough();

The philosophy: responses tolerate unknown fields. Without .passthrough(), Zod's default behavior strips unknown keys (and .strict() would reject them outright). With it, when the AIRS API ships a new response field, an already-installed SDK version:

  1. still validates the response (no RESPONSE_VALIDATION error), and
  2. preserves the new field on the returned object, so advanced callers can read it via an index even before the SDK adds a typed accessor.

This decouples the API's release cadence from the SDK's. Known fields remain validated: a server-side type change can still raise a response-validation error. Passthrough preserves additional fields; it does not weaken validators for declared properties.

Relationship to the OpenAPI specs and the Python SDK​

The SDK is modeled directly from Palo Alto Networks' public OpenAPI specifications. They are not vendored into this repository: schemas/ is a gitignored symlink to a local checkout of the openapi-specs/ directory in pan.dev, and the explicit MODELED_SPECS list in scripts/preflight-schemas.ts names the files that back the models:

schemas/ → <pan.dev checkout>/openapi-specs/
prisma-airs/scan/scan-service_latest.yaml # scan API
prisma-airs/management/mgmt-service_latest.yaml # management API
prisma-airs-model-security/dataplane/data-plane.yml # model security (data)
prisma-airs-model-security/management/mgmt-plane.yml # model security (mgmt)
prisma-airs-redteam/data-plane/dp-openapi.yaml # red team (data)
prisma-airs-redteam/management/mp-openapi.yaml # red team (mgmt)
prisma-airs-redteam/network-broker/AIRS-Red-Teaming-Network-Broker.yaml
dlp/dlp-api-spec-v2.yaml # DLP data filtering profiles
dlp/DataPatterns.yaml dlp/DataProfiles.yaml dlp/Dictionaries.yaml

The gateway comparison uses the supplied Portkey OpenAPI document, configured through GATEWAY_OPENAPI_FILE. Prisma SCM is an adaptation: route planes, authentication, envelopes and lifecycle behavior require separate live verification. The runtime client uses its explicit endpoint and API key, never SCM OAuth. See the complete gateway ledger for implemented routes versus adaptations and unverified gaps.

These specs are the source of truth for shapes. The Zod schemas in src/models/ are the hand-written, runtime-validating counterpart, and the preflight script (below) checks that the two agree.

The SDK also tracks the official Python pan-aisecurity SDK conceptually — files like src/errors.ts and src/constants.ts carry mirrors Python SDK comments — but it deliberately extends beyond it: the Python SDK covers scanning, whereas this SDK covers AI Runtime Security, Model Security, AI Red Teaming, and the AI Gateway, plus configuration/DLP management.

Why some DLP specs are excluded

openapi-specs/dlp/ contains more yaml than the SDK models. MODELED_SPECS lists only the four files that back implemented resources (dlp-api-spec-v2.yaml for data filtering profiles, plus 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​

npm run preflight now runs the frozen operation-contract tests (npm run openapi:check): exact requests, paths, query serialization, request validation and response validation. These tests run offline in CI without the gitignored source checkout.

npm run preflight # frozen operation-contract gate, also in CI
npm run openapi:audit # fresh local source audit with hashes
npm run preflight:legacy # historical component-name comparison, not coverage

The fresh audit reads AIRS_OPENAPI_DIR and GATEWAY_OPENAPI_FILE (with the local schemas/ alias as an AIRS fallback). It records source SHA-256 hashes, enumerates operations, counts typed request/response properties, and validates independently generated positive/negative fixtures. Optional declared properties count too: passthrough is not typed property coverage. The audit distinguishes the supplied seven AIRS contracts from supplemental DLP models and the complete 242-operation Portkey inventory.

Compatibility corrections have explicit source pointers and rationales in scripts/openapi/compatibility.ts; runtime-only corrections are also recorded in the generator and inference guide. Do not silently edit an upstream spec or weaken a request constraint to accommodate a response variation. Frozen fixtures under test/openapi/ make the contract reproducible without upstream network access.

The historical component-name comparison remains only as preflight:legacy and its warning-only variant. Its allowlist and optional-field omissions are not coverage evidence. It requires local source files and is not the current CI gate. See OpenAPI conformance for exact denominators: passing the AIRS gate does not mean the complete Portkey API is implemented.

Documentation checks​

The Docusaurus site lives in docs-site/. The API reference is generated from the public barrel export (src/index.ts) with TypeDoc before docs are served or built:

npm run docs:api # generate docs-site/docs/reference/api
npm run docs:serve # generate API docs, then start Docusaurus
npm run docs:build # generate API docs, then build the static site

The project also enforces JSDoc examples on public methods and exported functions:

npm run docs:check # strict example-coverage gate
npm run docs:check:warn # report missing examples without failing

The docs deploy workflow runs the example-coverage check, regenerates TypeDoc output, and builds Docusaurus for GitHub Pages.

Changeset-driven releases​

Every user-facing change ships with a changeset: a small markdown file in .changeset/. The format is a YAML front-matter block declaring the bump level, followed by a user-facing description:

---
'@cdot65/prisma-airs-sdk': minor
---

**Breaking type changes** to `ScanResponse.tool_detected` to match the actual AIRS API shape
(verified against a live response).

### Migration

If you read tool flags from `tool_detected.input_detected.<flag>`, walk `detection_entries` instead.

Conventions in this repo:

  • Bump level is the one word after the package name: patch, minor, or major. Pick it per the pre-1.0 rules above — breaking type reshapes are minor, fixes/internal/additive are patch.
  • Description is user-facing: state what was added or fixed, and include a Migration section for any breaking change (existing changesets like 0017-tool-detected-reshape.md are the template).
  • Internal-only work (e.g. the preflight gate, 0011-preflight-schema-check.md) is still recorded as a patch with an explicit "no public API change" note.

Changesets record the intended bump and release-note content. The SDK does not currently install a Changesets versioning CLI: its release operator must update package.json and the lockfile together with the normal npm version workflow, then create the matching GitHub release. The companion CLI has its own Changesets-managed workflow. prepublishOnly runs lint + test as a final guard, and the package publishes via OIDC trusted publishing on a GitHub release — no npm tokens.