Metadata-Version: 2.4
Name: declarative-sdk-for-k
Version: 2.21.0
Summary: Declarative lifecycle library for Keeper tenant state — adopt, plan, apply, diff, drift-watch, audit-explain. Terraform-style workflow for Keeper PAM, vault, MSP, and KSM.
Author-email: Martin Sawczyn <martin@augenblik.eu>
Maintainer-email: Martin Sawczyn <martin@augenblik.eu>
License-Expression: MIT
Project-URL: Homepage, https://github.com/msawczynk/dsk
Project-URL: Repository, https://github.com/msawczynk/dsk
Project-URL: Documentation, https://msawczynk.github.io/dsk/
Project-URL: Issues, https://github.com/msawczynk/dsk/issues
Project-URL: Changelog, https://github.com/msawczynk/dsk/blob/main/CHANGELOG.md
Keywords: keeper,vault,pam,ksm,enterprise,declarative,iac,agents,llm,terraform-like
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: click>=8.1
Requires-Dist: cryptography<49,>=42
Requires-Dist: pydantic>=2.0
Requires-Dist: networkx>=3.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0
Requires-Dist: jsonschema>=4.21
Requires-Dist: keepercommander<19,>=18.0
Requires-Dist: protobuf<7,>=4.25
Requires-Dist: pyotp
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: ruff==0.15.10; extra == "dev"
Requires-Dist: bandit>=1.9; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"
Requires-Dist: types-jsonschema; extra == "dev"
Requires-Dist: hypothesis; extra == "dev"
Requires-Dist: pytest-benchmark; extra == "dev"
Requires-Dist: mutmut; extra == "dev"
Requires-Dist: deptry; extra == "dev"
Requires-Dist: mkdocs; extra == "dev"
Requires-Dist: mkdocs-material; extra == "dev"
Requires-Dist: cyclonedx-bom; extra == "dev"
Provides-Extra: ksm
Requires-Dist: keeper-secrets-manager-core<18,>=17.2.0; extra == "ksm"
Provides-Extra: mcp
Requires-Dist: mcp[cli]>=1.0.0; extra == "mcp"
Provides-Extra: hcl
Requires-Dist: python-hcl2>=4.0.0; extra == "hcl"
Provides-Extra: spiffe
Requires-Dist: PyJWT<3,>=2.8; extra == "spiffe"
Requires-Dist: requests<3,>=2.31; extra == "spiffe"
Provides-Extra: slack
Requires-Dist: requests<3,>=2.31; extra == "slack"
Provides-Extra: langchain
Requires-Dist: langchain<2,>=0.3; extra == "langchain"
Provides-Extra: service
Requires-Dist: cryptography<49,>=42; extra == "service"
Provides-Extra: webui
Requires-Dist: fastapi>=0.110; extra == "webui"
Requires-Dist: uvicorn[standard]>=0.27; extra == "webui"
Dynamic: license-file

# Keeper Declarative SDK (DSK)

[![CI](https://github.com/msawczynk/dsk/actions/workflows/ci.yml/badge.svg)](https://github.com/msawczynk/dsk/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/declarative-sdk-for-k.svg)](https://pypi.org/project/declarative-sdk-for-k/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

> **Not an official Keeper Security product.** DSK is an independent community tool. It is not endorsed by, affiliated with, or supported by Keeper Security, Inc. For official Keeper tooling see the [Keeper Terraform provider](https://registry.terraform.io/providers/Keeper-Security/keeper/latest) and [Keeper Commander](https://github.com/Keeper-Security/Commander).

## Status

DSK is in beta and being prepared for absorption into Keeper Security's
official tooling. See [`HANDOVER.md`](HANDOVER.md) for the absorption intent
and current status.

> **For Keeper executive review:** see [`docs/SHOWCASE-FOR-CRAIG.md`](docs/SHOWCASE-FOR-CRAIG.md).

- [**Demo for Keeper Engineering**](docs/CRAIG-DEMO.md) — 10-minute hands-on walkthrough

Security policy: [`SECURITY.md`](SECURITY.md). Contribution guide:
[`CONTRIBUTING.md`](CONTRIBUTING.md).

## What is this, in plain English?

**At a glance:** `validate` → `plan` → `apply` (see [`docs/QUICK_START.md`](docs/QUICK_START.md)), plus `import` / `export` and reports. Copy-paste examples live in [`docs/EXAMPLES.md`](docs/EXAMPLES.md) and the [`examples/`](examples/) tree. To see a keeperCMD run-dir work in five minutes, start with [`examples/01-verify-existing-rundir/`](examples/01-verify-existing-rundir/).


**DSK lets you describe what your Keeper tenant should look like in a YAML
file, then asks the SDK to make the live tenant match.**

The Python library API is two-tiered: the narrow absorption API for
`keeper_tenant_migrate` is documented in [`LIBRARY_API.md`](LIBRARY_API.md),
while the wider standalone DSK modules remain available outside that
absorption contract.

You write a manifest (one file, plain YAML) that lists the gateways,
configurations, machines, databases, vault records, KSM apps, MSP
companies, and policies you want. You then run three commands:

| Command | What it does | Plain-English meaning |
|---------|--------------|-----------------------|
| `dsk validate manifest.yaml` | Schema + reference + capability checks | "Is this file even legal?" |
| `dsk plan manifest.yaml` | Compares manifest to live tenant | "What would change if I applied this?" |
| `dsk apply manifest.yaml` | Executes the plan | "Make the tenant match the file." |

That's the whole loop. It is the same loop Terraform uses for cloud
infrastructure — DSK is the equivalent for Keeper PAM, vault, MSP, and
KSM. You can preview every change before it runs, and DSK only ever
touches records it can prove it owns (via ownership markers it writes
when you import or apply).

DSK is **complementary to Terraform, not a replacement.** If you already
use the official [Keeper Terraform provider](https://registry.terraform.io/providers/Keeper-Security/commander/latest/docs)
for some resources, keep using it; DSK can sit beside it for everything
the Terraform provider doesn't yet cover, or for teams that prefer a
single Python-native loop with explicit human-in-the-loop confirmation.
See [`docs/TERRAFORM_INTEGRATION.md`](docs/TERRAFORM_INTEGRATION.md) for
ownership-boundary guidance.

---

## Why use it?

- **Reviewable change.** Every mutation is a YAML diff in a pull request.
- **Safe by default.** `plan` is dry-run; `apply` requires `--auto-approve`
  or an interactive prompt; deletes need an explicit `--allow-delete`.
- **Mock provider for CI.** Run the full loop offline against a fake
  tenant — no Keeper credentials needed in pull-request checks.
- **Live provider for production.** When you're ready, point the same
  manifests at a real Keeper tenant via the bundled Commander
  integration.
- **Typed exit codes.** CI pipelines branch on integers, not regexes.
- **Built for AI agents.** Every command has a `--json` mode and a
  documented JSON contract; see [`AGENTS.md`](AGENTS.md).

---

## Install and verify

```bash
pip install declarative-sdk-for-k
dsk --version
dsk doctor
```

Need a specific git ref or extras (HCL, MCP, KSM)? See
[`docs/INSTALLATION.md`](docs/INSTALLATION.md).

---

## A walkthrough you can copy-paste

Save this as `acme-lab.yaml`:

```yaml
schema: pam-environment.v1
name: acme-lab
resources:
  - uid_ref: gw.lab
    resource_type: pamGateway
    title: Lab Gateway
  - uid_ref: cfg.aws
    resource_type: pamConfiguration
    title: AWS PAM Config
    gateway_uid_ref: gw.lab
  - uid_ref: db.prod
    resource_type: pamDatabase
    title: prod-mysql
    pam_configuration_uid_ref: cfg.aws
    host: prod.example.com
    port: 3306
```

Run the loop offline against the mock provider (no credentials needed):

```bash
dsk validate acme-lab.yaml --provider mock
dsk plan     acme-lab.yaml --provider mock
dsk apply    acme-lab.yaml --provider mock --auto-approve
```

`validate` confirms the file is well-formed. `plan` prints a table of
what would change. `apply` performs those changes against the in-memory
mock tenant.

When you're ready to point at the real tenant, drop `--provider mock`
and export the credentials (see [`docs/LOGIN.md`](docs/LOGIN.md)):

```bash
export KEEPER_EMAIL='you@example.com'
export KEEPER_PASSWORD='...'
export KEEPER_TOTP_SECRET='BASE32SECRET'   # not the 6-digit code

dsk validate acme-lab.yaml --online
dsk plan     acme-lab.yaml
dsk apply    acme-lab.yaml --auto-approve
```

The five-minute walkthrough lives at
[`docs/QUICK_START.md`](docs/QUICK_START.md).

---

## Common tasks (recipe book)

### 1. "I want to see what would change without changing anything."

```bash
dsk plan acme-lab.yaml --json | jq '.summary'
```

Exit `0` = clean, `2` = changes present, `4` = conflicts. Full JSON shape
in [`AGENTS.md`](AGENTS.md#json-contracts-agents-can-parse).

### 2. "I want a field-level diff before approving."

```bash
dsk diff acme-lab.yaml
```

Secrets are redacted at the renderer; raw values never leave the process.

### 3. "I have records that already exist in Keeper. Adopt them."

```bash
dsk import acme-lab.yaml --dry-run     # preview adoptions
dsk import acme-lab.yaml --auto-approve
```

Adoption only matches records with no existing ownership marker.

### 4. "I want to lift an existing tenant into a manifest."

```bash
dsk export project.json -o acme-lab.yaml
```

`project.json` is a Commander-shaped PAM project export. The output is a
manifest you can edit, commit, and re-apply.

### 5. "Run a CI gate that fails on conflict but allows clean changes."

```bash
dsk plan acme-lab.yaml --json > plan.json
jq -e '.summary.conflict == 0' plan.json
```

Or use the bundled [GitHub Action](.github/actions/dsk/README.md):

```yaml
- uses: msawczynk/dsk/.github/actions/dsk@main
  with:
    manifest-path: manifests/acme-lab.yaml
    command: plan
```

### 6. "Watch for drift continuously."

```bash
DSK_PREVIEW=drift-watch dsk drift-watch manifests/*.yaml
```

### 7. "Generate a compliance evidence bundle."

```bash
dsk bundle manifests/acme-lab.yaml -o evidence/
```

### 8. "Run a Keeper Commander report from DSK with redaction."

```bash
dsk report password-report --quiet
dsk report compliance-report --node 12345
dsk report security-audit-report --record-details
```

### 9. "Expose DSK to keeper-migrate."

Hybrid Option 3 keeps standalone DSK intact and adds a shim layer that
`keeper-migrate` can discover:

```bash
dsk shim-info
```

Python embedders can use:

```python
from dsk import shim

info = shim.shim_info()
```

See [`docs/architecture/keeper-migrate-shim-design.md`](docs/architecture/keeper-migrate-shim-design.md).

### 10. "Use it from Python, not the CLI."

```python
from dsk.core import (
    load_manifest, build_graph, build_plan, compute_diff,
)
from dsk.providers import MockProvider

manifest = load_manifest("acme-lab.yaml")
graph = build_graph(manifest)
provider = MockProvider()
plan = build_plan(manifest, graph, provider)
print(f"creates={plan.summary.create} updates={plan.summary.update}")
```

More entry points (vault, MSP, KSM): see
[`docs/QUICK_START.md`](docs/QUICK_START.md#programmatic-load) and the
package overview in [`dsk/SCAFFOLD.md`](dsk/SCAFFOLD.md).

---

## CLI commands at a glance

| Command | Role |
|---------|------|
| `dsk validate PATH` | Schema, references, optional `--online` tenant checks |
| `dsk plan PATH` | Plan vs tenant; `--json` for machine-readable |
| `dsk diff PATH` | Field-level diff (redacted) |
| `dsk apply PATH` | Execute plan; `--dry-run`, `--auto-approve`, `--allow-delete` |
| `dsk import PATH` | Adopt unmarked live records |
| `dsk import-from-keepercmd RUN_DIR` | Import keeperCMD migration run-dir into DSK manifests |
| `dsk verify RUN_DIR` | Verify keeperCMD run-dir audit chain, sidecars, checksums, and contract version |
| `dsk export FILE.json` | Commander-shaped PAM JSON → manifest YAML |
| `dsk discover` | List unmanaged PAM resources |
| `dsk scan` | Surface unmanaged identities and configuration drift |
| `dsk drift-watch PATH...` | Drift daemon (preview-gated) |
| `dsk bundle` | Compliance evidence bundle |
| `dsk audit explain AUDIT_LOG` | Inspect keeperCMD audit-chain events and failures |
| `dsk rehearse-report RUN_DIR` | Emit keeperCMD rehearsal drift report / JUnit stub |
| `dsk report ...` | Password / compliance / security-audit / vault-health / KSM / team / role reports |
| `dsk spiffe-verify` | Verify SPIFFE JWT-SVID bindings |
| `dsk refusal` | Run refusal policy checks on manifests |
| `dsk panic-revoke` | Mark emergency revocation intent in plan inputs |
| `dsk live-smoke` | Run the committed live-tenant smoke harness |
| `dsk run ...` | Commander passthrough with redaction |
| `dsk bootstrap-ksm` | KSM bootstrap helper |
| `dsk doctor` | Environment / dependency snapshot |
| `dsk orient` | Short orientation for agents |
| `dsk mcp serve` | MCP (Model Context Protocol) server |
| `dsk webui` | Browser-based front-end (Keeper-inspired, dark-mode toggle, streaming + cancel) |

`dsk rehearse-report` currently has a text-mode stub while D1 fixture work is
pending; `--format junit` emits the CI-consumable rehearsal status XML.

Every command supports `--help`. JSON shapes and exit-code contracts:
[`AGENTS.md`](AGENTS.md).

---

## What can DSK manage?

The manifest catalog splits into two tiers. **Tier 1** families are backed by a live Keeper API today — `validate`, `plan`, and `apply` all work against a real tenant (live-smoke evidence cited per row). **Tier 2** families have a real schema and useful `validate` + `plan` output, but `apply` exits with a typed `CapabilityError` because the Keeper API doesn't exist yet, or because the family targets an external system (Slack, ITSM, SIEM, CI, OPA). Nothing silently drops; both tiers are honest about their limits.

> **Not an official Keeper Security product.** DSK is a community tool. It is not endorsed by, affiliated with, or supported by Keeper Security, Inc. For official Keeper tooling see the [Keeper Terraform provider](https://registry.terraform.io/providers/Keeper-Security/keeper/latest) and [Keeper Commander](https://github.com/Keeper-Security/Commander).

### Tier 1 — Keeper-API-backed (live-proven)

| Family | Live-smoke evidence | Status |
|---|---|---|
| `pam-environment.v1` | E2E green (28 Apr 2026) — pamMachine, pamDatabase, pamDirectory, pamRemoteBrowser | supported |
| `keeper-vault.v1` | vault login record create→verify→destroy (28 Apr 2026) | supported |
| `keeper-vault-sharing.v1` | shared folder create + membership grant (30 Apr 2026) | supported |
| `keeper-ksm.v1` | KSM bootstrap + app create + KsmLoginHelper (28 Apr 2026) | supported (app lifecycle preview-gated) |
| `msp-environment.v1` | MC add/update/remove via Commander (1 May 2026) | supported |
| `keeper-epm.v1` | EPM policy add/verify/delete on lab tenant (1 May 2026) | supported (watchlists/approvers upstream-gap) |
| `keeper-workflow.v1` | workflow create/verify/delete on lab tenant (1 May 2026) | preview-gated → supported in progress |
| `jit-access.v1` | pam project import jit_settings path (Commander 17.2.7+) | supported plan+apply; readback upstream-gap |
| `rotation-policy.v1` | PAMCreateRecordRotationCommand path (Commander 18.0.0) | supported apply; readback upstream-gap |
| `keeper-enterprise.v1` | offline foundation — nodes/users/roles/teams/enforcements | preview-gated (online apply pending) |
| `keeper-pam-extended.v1` | offline foundation — gateway configs/rotation schedules/discovery rules | preview-gated |
| `keeper-integrations-identity.v1` | offline foundation — domains/SCIM/SSO/outbound email | preview-gated / upstream-gap |
| `keeper-integrations-events.v1` | offline foundation — automator rules/audit alerts/API keys | preview-gated / upstream-gap |

### Tier 2 — Forward-spec / delivery-only (validate + plan only; apply refused)

`apply` exits with `CapabilityError` — the schema is real and produces useful plan output, but the Keeper API or target system write path does not exist yet.

| Family | Why apply is refused | Delivery target |
|---|---|---|
| `ai-act-profile.v1` | No Keeper AI-Act API | spec / OPA gate output |
| `dora-profile.v1` | No Keeper DORA API | spec / OPA gate output |
| `cmmc-profile.v1` | No Keeper CMMC API | spec / OPA gate output |
| `pqc-policy.v1` | No Keeper PQC API | spec |
| `spiffe-binding.v1` | No Keeper SPIFFE API | spec / OPA |
| `nhi-agent.v1` | Keeper NHI PAM API not GA | spec |
| `ai-token.v1` | No Keeper AI-token API | spec / external IDP |
| `ai-agent-trust-chain.v1` | No Keeper agent-trust API | spec |
| `keeper-ai-policy.v1` | No Keeper AI-policy API | spec |
| `agentic-skill-policy.v1` | No Keeper skill-policy API | spec / external |
| `agent-memory-policy.v1` | No Keeper memory-policy API | spec / external |
| `mcp-server-allowlist.v1` | No Keeper MCP-allowlist API | spec / OPA |
| `mcp-secrets-binding.v1` | No Keeper MCP-binding API | spec |
| `compliance-bundle.v1` | Aggregator over compliance specs above | spec |
| `continuous-evidence-stream.v1` | External SIEM, no Keeper API | k8s / SIEM REST |
| `cspm-remediation.v1` | External CSPM | external |
| `pipeline-ephemeral-environment.v1` | External CI | external |
| `cloud-jit.v1` | No multi-cloud JIT writer in Commander | preview-gated |
| `workflow-gate.v1` / `slack-approval-gate.v1` / `itsm-approval-gate.v1` | External Slack / ITSM | external REST |
| `secret-scanner-bridge.v1` | External scanner (GHAS → PAM rotation bridge) | external |
| `pam-connection-profile.v1` | Commander post-import mutation gap | preview-gated / upstream-gap |
| `db-access-policy.v1` | KeeperDB session policy live proof pending | preview-gated |
| `gateway-ha.v1` | No Commander HA gateway writer | preview-gated |
| `keeper-scim.v1` | Enterprise SCIM apply upstream-gap | preview-gated / upstream-gap |

---

## Providers

| Provider | Use |
|----------|-----|
| `mock` | Offline graph, deterministic UIDs, no network. Use this in CI. |
| `commander` | Live Keeper via the `keepercommander` Python package (pinned in `pyproject.toml`). |

Login helpers, KSM bootstrap, and custom auth: [`docs/LOGIN.md`](docs/LOGIN.md).

---

## Testing

- **In-repo (always current):** start with [`docs/QUICK_START.md`](docs/QUICK_START.md) and the **examples index** [`docs/EXAMPLES.md`](docs/EXAMPLES.md). Full doc set: [`docs/index.md`](docs/index.md) (also built as a [MkDocs](https://www.mkdocs.org/) site from this tree).

DSK ships with **3,500+ unit and integration tests** that exercise every
manifest family, every CLI subcommand, the mock provider, the Commander
provider's stubbed surfaces, and the JSON-output contracts. The test
suite is the binding contract — if a feature isn't covered by a test,
treat it as preview.

Run the full suite:

```bash
pip install -e ".[dev]"
pytest                       # full suite (~30s)
pytest --cov=dsk      # with coverage
ruff check . && ruff format --check .
mypy dsk
```

The committed entry point for local merge gates is
[`scripts/phase_harness/run_local_gates.sh`](scripts/phase_harness/run_local_gates.sh).
CI runs the same gates plus an examples-validation matrix on every PR
(see [`.github/workflows/ci.yml`](.github/workflows/ci.yml)).

For live-tenant smoke runs against a real Keeper tenant, the
committed harness is [`scripts/smoke/`](scripts/smoke/) and the
runbook is [`docs/LIVE_TEST_RUNBOOK.md`](docs/LIVE_TEST_RUNBOOK.md).

---

## Documentation map

| You want to... | Read |
|----------------|------|
| Install and verify | [`docs/INSTALLATION.md`](docs/INSTALLATION.md) |
| Five-minute walkthrough | [`docs/QUICK_START.md`](docs/QUICK_START.md) |
| Login and providers | [`docs/LOGIN.md`](docs/LOGIN.md) |
| Exit codes and validation stages | [`docs/VALIDATION_STAGES.md`](docs/VALIDATION_STAGES.md) |
| Honest capability matrix | [`docs/SDK_DA_COMPLETION_PLAN.md`](docs/SDK_DA_COMPLETION_PLAN.md) |
| Commander coverage and pin | [`docs/COMMANDER.md`](docs/COMMANDER.md) |
| GitHub Actions integration | [`docs/GITHUB_ACTIONS.md`](docs/GITHUB_ACTIONS.md) |
| Terraform side-by-side | [`docs/TERRAFORM_INTEGRATION.md`](docs/TERRAFORM_INTEGRATION.md) |
| Pulumi / Crossplane / k8s | [`pulumi/`](pulumi/), [`crossplane/`](crossplane/), [`kubernetes/`](kubernetes/) |
| Backstage plugin | [`backstage-plugin-dsk/`](backstage-plugin-dsk/) |
| Web UI (browser front-end, dark mode) | [`webui/`](webui/) |
| Agent / LLM contract | [`AGENTS.md`](AGENTS.md) |
| Library API and semver contract | [`LIBRARY_API.md`](LIBRARY_API.md), [`SEMVER.md`](SEMVER.md) |
| Glossary and naming conventions | [`docs/glossary.md`](docs/glossary.md) |
| ADR index | [`docs/adr/README.md`](docs/adr/README.md) |
| Threat model | [`docs/security/threat-model.md`](docs/security/threat-model.md) |
| Compliance control mapping | [`docs/compliance/control-mapping.md`](docs/compliance/control-mapping.md) |
| Changelog | [`CHANGELOG.md`](CHANGELOG.md) |
| Keeper engineer onboarding | [`docs/onboarding-for-keeper-engineers.md`](docs/onboarding-for-keeper-engineers.md) |
| keeperCMD run-dir tutorials | [`examples/01-verify-existing-rundir/`](examples/01-verify-existing-rundir/), [`examples/02-import-from-keepercmd/`](examples/02-import-from-keepercmd/), [`examples/03-rehearse-vs-apply/`](examples/03-rehearse-vs-apply/) |
| Raw keeperCMD migration guide | [`docs/migration-from-raw-keepercmd.md`](docs/migration-from-raw-keepercmd.md) |
| Wave 2 promotion safety | [`docs/wave2-promotion-runbook.md`](docs/wave2-promotion-runbook.md) |
| OUTPUT_CONTRACT v1.2 absorption | [`docs/output-contract-v1.2-absorption-runbook.md`](docs/output-contract-v1.2-absorption-runbook.md) |
| Per-folder scaffold (where to land work) | [`SCAFFOLD.md`](SCAFFOLD.md) |
| Reconciliation against design + checklists | [`RECONCILIATION.md`](RECONCILIATION.md) |
| Roadmap | [`docs/DSK_NEXT_WORK.md`](docs/DSK_NEXT_WORK.md) |

## Security & supply chain

Release builds publish a CycloneDX JSON SBOM as a GitHub Release artifact
(`sbom.json`). Generate the same file locally with:

```bash
pip install -e ".[dev]"
make sbom
```

Release artifact link pattern:
`https://github.com/msawczynk/dsk/releases/download/<tag>/sbom.json`

---

## Contributing

DSK is autonomous-agent friendly: every change should be reproducible by
running the local gates above. The agent operating manual is
[`AGENTS.md`](AGENTS.md). Contribution guidance is in
[`CONTRIBUTING.md`](CONTRIBUTING.md). Issues and PRs welcome at
[`msawczynk/dsk`](https://github.com/msawczynk/dsk).

---

## License

MIT — see [`LICENSE`](LICENSE).
