pckg client contract
Contract surface for the Beskid package registry client crate used by tooling workflows.
Document layout: Feature capability layoutconforms
## Purpose## Requirements
pckg client contract Specification
Purpose
Contract surface for the Beskid package registry client crate used by tooling workflows.
Requirements
Requirement: Hub authority: Decision [D-TOOL-PCKG-0001]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Hub owns HTTP and auth.
Stable ID: BSP-REQ-4C374A0F8991
Legacy source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/adr/0001-hub-authority/content.md
Source SHA-256: 16eeeaea148e9201cc4c515fbb8b107ed86466a2f99bd5781411990eaa2c1b63
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Registry versions: Decision [D-TOOL-PCKG-0002]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Registry-assigned publish versions in routine flows.
Stable ID: BSP-REQ-96F30E1AED03
Legacy source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/adr/0002-registry-assigned-versions/content.md
Source SHA-256: b202b1e7377a19ac989aefe5f1b07e702cf42377fa2db20c4d287616385c0a84
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: api.json symbolKey pack validation: Decision [D-TOOL-PCKG-0005]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
validate_packed_api_docmust runvalidate_symbol_keysforschemaVersion >= 4graph artifacts. WhensymbolKeyis present on an item:
Check Rule Non-empty Reject blank strings Package prefix Must contain ::(package-prefixed canonical form)Uniqueness Reject duplicate symbolKeyvalues across itemsLeaf consistency Lenient check: obvious mismatch between symbolKeyleaf segment andqualifiedNameleaf may reject when unambiguousAbsence of
symbolKeyon any row remains valid (backward compatibility with pre-registry emitters).
Stable ID: BSP-REQ-BBEFDCD80BA7
Legacy source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/adr/0005-api-json-symbol-key-validation/content.md
Source SHA-256: dd618229faa546df91192be63fb37ac8dc9a4e66451d51f81bca590fd6bf5aa9
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Informative Source Provenance
The records below preserve migration history and are not normative except where text was extracted into a requirement above.
Source Record: pckg client contract
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/content.md
SHA-256: 864c73fb43cef0388cc6a8fdae95bf25be89d371da8ce07c4bd527bf283b6145
Migrated source text
<SpecSection title="What this feature specifies" id="what-this-feature-specifies">
`pckg client contract` defines one operational contract that a newcomer can follow end-to-end: first the model, then execution flow, then strict guarantees, concrete examples, and verification guidance.
</SpecSection>
<SpecSection title="Implementation anchors" id="implementation-anchors">
- Public client API in `compiler/crates/beskid_pckg/src/lib.rs`
- HTTP and auth flows in `compiler/crates/beskid_pckg/src/client.rs`
- CLI integration in `compiler/crates/beskid_pckg/src/cli.rs`
- Package dashboard behavior in `compiler/crates/beskid_pckg_server/Components/Pages/Dashboard/Packages.razor.cs`
</SpecSection>
<SpecSection title="Decisions" id="decisions">
No open decisions. **`D-TOOL-PCKG-0001`** (hub authority), **`0002`** (registry-assigned versions), **`0005`** (`api.json` **`symbolKey`** validation)—see **`adr/`** and the **ADRs** tab.
</SpecSection>
## Decisions
<!-- spec:generate:adr-index -->
No open decisions. Closed choices are normative ADRs under **`adr/`** (`D-TOOL-PCKG-0001` … `D-TOOL-PCKG-0005`); use the reader **ADRs** tab for expandable detail.
<!-- /spec:generate:adr-index -->
## Articles
<!-- spec:generate:article-index -->
- [Contracts and edge cases](./articles/contracts-and-edge-cases/)
- [Design model](./articles/design-model/)
- [Examples](./articles/examples/)
- [FAQ and troubleshooting](./articles/faq-and-troubleshooting/)
- [Flow and algorithm](./articles/flow-and-algorithm/)
- [Verification and traceability](./articles/verification-and-traceability/)
<!-- /spec:generate:article-index -->
Source Record: Hub authority
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/adr/0001-hub-authority/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/adr/0001-hub-authority/content.md
SHA-256: 16eeeaea148e9201cc4c515fbb8b107ed86466a2f99bd5781411990eaa2c1b63
Migrated source text
## Context
One client story.
## Decision
Hub owns HTTP and auth.
## Consequences
Extension defers.
## Verification anchors
beskid_pckg; pckgClient.
Source Record: Registry versions
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/adr/0002-registry-assigned-versions/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/adr/0002-registry-assigned-versions/content.md
SHA-256: b202b1e7377a19ac989aefe5f1b07e702cf42377fa2db20c4d287616385c0a84
Migrated source text
## Context
Manual semver drift.
## Decision
Registry-assigned publish versions in routine flows.
## Consequences
UI shows server version.
## Verification anchors
publish API tests.
Source Record: api.json symbolKey pack validation
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/adr/0005-api-json-symbol-key-validation/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/adr/0005-api-json-symbol-key-validation/content.md
SHA-256: dd618229faa546df91192be63fb37ac8dc9a4e66451d51f81bca590fd6bf5aa9
Migrated source text
## Context
Registry ingestion already validates graph navigation (`parentId`, `refItemId`, package-relative paths). With registry-backed **`symbolKey`** on v4 rows, pack-time validation must catch malformed or duplicated keys before artifacts reach the docs UI.
## Decision
`validate_packed_api_doc` **must** run **`validate_symbol_keys`** for `schemaVersion >= 4` graph artifacts. When `symbolKey` is present on an item:
| Check | Rule |
| --- | --- |
| Non-empty | Reject blank strings |
| Package prefix | Must contain `::` (package-prefixed canonical form) |
| Uniqueness | Reject duplicate `symbolKey` values across items |
| Leaf consistency | Lenient check: obvious mismatch between `symbolKey` leaf segment and `qualifiedName` leaf **may** reject when unambiguous |
Absence of `symbolKey` on any row **remains valid** (backward compatibility with pre-registry emitters).
## Consequences
- Implementation: `compiler/crates/beskid_pckg/src/api_doc.rs`
- Operators re-pack with a current `beskid doc` / CLI to populate keys on new publishes; old `.bpk` files without keys still ingest.
## Verification anchors
- `compiler/crates/beskid_pckg/src/api_doc.rs` unit tests (accept distinct keys, reject duplicate / malformed, accept omitted field)
Source Record: Contracts and edge cases
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/articles/contracts-and-edge-cases/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/articles/contracts-and-edge-cases/content.md
SHA-256: a3542b970f3ebed643de20b370ee2db7151d7ff25634288586abcf0887d76556
Migrated source text
## Contracts
- **HTTPS in production** — Operators **should** configure `BESKID_PCKG_URL` with TLS endpoints; clients honor redirects per reqwest defaults.
- **Auth mutual exclusion** — Bearer and API key flags **must not** be combined on one invocation.
- **Structured `api.json`** — Library packs **should** include compiler-emitted `.beskid/docs/api.json` as the docs UI contract (see **[api.json contract](/platform-spec/tooling/cli/api-json-contract/)**). When **`symbolKey`** is present on v4 rows, pack validation **must** enforce registry invariants ([D-TOOL-PCKG-0005](/platform-spec/tooling/registry-client/pckg-client-contract/adr/0005-api-json-symbol-key-validation/)).
- **Readme artifact name** — Packed readmes **must** appear as root `README.md` in the zip for dashboard listing.
- **Template discriminator** — Template packs **must** set `packageKind: template` and ship `.beskid/template.json`.
## Edge cases
| Case | Behavior |
| --- | --- |
| Missing `Project.proj` at pack root | Defaults to library profile without template metadata |
| Invalid `api.json` graph / symbolKey | `validate_packed_api_doc` rejects before ingest (duplicate or malformed **`symbolKey`**, broken `parentId`, absolute paths) |
| Missing auth on publish | `MissingAuthToken` before HTTP |
| Large trees | Walkdir-based collection; operators exclude build outputs via manifest/layout conventions |
| `iconUrl` on publish | Server validates HTTP(S) URLs and length caps (`PackageManifestMetadata`) |
## Server rejection mirrors
Client-side pack **should** prevalidate paths the server will reject (docs layout, workspace member consistency) to avoid wasted uploads; integration tests live in `compiler/crates/beskid_pckg_server/tests/`.
Source Record: Design model
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/articles/design-model/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/articles/design-model/content.md
SHA-256: 4529f877e3e6f438814b57a2b46d888128d3103dbc42f1df1fb8391b37c56184
Migrated source text
## Purpose
`beskid_pckg` is the single HTTP client for the **pckg** registry service. The CLI exposes it as `beskid pckg <subcommand>`; analysis-time fetch uses the same `PckgClient` types. Server behavior for published artifacts is implemented in `compiler/crates/beskid_pckg_server/` (validation, docs ingestion, workspace bundles).
## Component model
```mermaid
flowchart LR
CLI[beskid pckg CLI]
Client[PckgClient]
CFG[PckgClientConfig]
HTTP[reqwest]
SVC[pckg Server API]
CLI --> Client
CFG --> Client
Client --> HTTP --> SVC
```
| Piece | Role |
| --- | --- |
| **`PckgClientConfig`** | `base_url`, timeout, `user_agent`, auth (`BearerToken` or `PublisherApiKey`) |
| **`PckgClient`** | JSON and multipart request builders with unified `PckgError` mapping |
| **`pack` module** | Walk tree → zip `.bpk`, inject `README.md`, build `package.json` (`PackProfile`) |
| **Repository config** | `.beskid/pckg/repositories.json` maps registry aliases to API keys |
## Authentication
Authenticated routes **must** receive either:
- `BESKID_PCKG_TOKEN` / `--bearer-token`, or
- `BESKID_PCKG_API_KEY` / `--api-key` (header name from config, used for publisher keys)
Missing auth on `require_auth` endpoints returns `PckgError::MissingAuthToken` without contacting the network.
## Pack profiles
| Profile | Detection | `package.json` |
| --- | --- | --- |
| **Library** | Default or non-`Template` `Project.proj` | Includes docs paths, `api.json` when present under `.beskid/docs/` |
| **Template** | `project.type = Template` | `packageKind: template` + summary from `.beskid/template.json` |
Readme resolution uses `discover_readme_for_package_root` from `beskid_analysis` (optional `readme` key or default `readme.md`).
## Server-side counterparts
| Concern | pckg path |
| --- | --- |
| Publish validation | `compiler/crates/beskid_pckg_server/Services/PackageArtifactValidator.cs` |
| Docs + `api.json` | `compiler/crates/beskid_pckg_server/Services/PackagePublishDocumentation.cs` |
| Workspace bundles | `compiler/crates/beskid_pckg_server/Services/Workspace/WorkspacePublishService.cs` |
| Dashboard listing | `compiler/crates/beskid_pckg_server/Components/Pages/Dashboard/Packages.razor.cs` |
## Implementation anchors
- `compiler/crates/beskid_pckg/src/lib.rs`, `client.rs`, `cli.rs`, `pack.rs`
- Models: `compiler/crates/beskid_pckg/src/models.rs`, `packages.rs`
Source Record: Examples
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/articles/examples/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/articles/examples/content.md
SHA-256: 49200f8160b8136fe34413f7c7cc42b2418f99e58d5d229d35072ef8100b1ca7
Migrated source text
## Pack a library project
```bash
beskid pckg pack --project compiler/corelib/beskid_corelib/Project.proj
```
Produces a `.bpk` with `README.md`, sources, and `.beskid/docs/` when present.
## Publish with API key
```bash
export BESKID_PCKG_URL=https://pckg.example/
export BESKID_PCKG_API_KEY=...
beskid pckg upload path/to/package.bpk
```
Exact subcommand names follow `PckgCommand` in `cli.rs` (use `beskid pckg --help` in installed CLI).
## Repository config file
`.beskid/pckg/repositories.json`:
```json
{
"repositories": {
"default": { "api_key": "..." }
}
}
```
Maps to registry aliases in `Workspace.proj`.
## CI corelib publish
Compiler CI uses `BESKID_PCKG_KEY` to publish the **`corelib`** package identity after build—same client stack as local `beskid pckg` with injected secrets.
Source Record: FAQ and troubleshooting
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/articles/faq-and-troubleshooting/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/articles/faq-and-troubleshooting/content.md
SHA-256: c1d06f1d6a9b0f052e0f489797e1a2296627434922e47ec7307e08758a5e3b7a
Migrated source text
## 401 / MissingAuthToken
Set `BESKID_PCKG_API_KEY` or bearer token. For multi-registry setups, ensure `.beskid/pckg/repositories.json` keys match `Workspace.proj` registry alias names.
## Pack missing docs on pckg site
Run `beskid doc --project ...` before pack so `.beskid/docs/api.json` exists. Server ingestion (`PackagePublishDocumentation`) treats that tree as first-class.
## Template pack rejected
Verify `project.type = Template`, `.beskid/template.json` (`beskid.template.v1`), and `packageKind: template` in generated `package.json`.
## Wrong package version after publish
Registry assigns versions in normal flows—do not expect local `project.version` alone to drive the published label. Query the server for the assigned version summary.
## Local dev server URL
Default `https://pckg.beskid-lang.org` points at the public registry; override with `BESKID_PCKG_URL` for local compose (`http://127.0.0.1:8082`) or other hosts.
Source Record: Flow and algorithm
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/articles/flow-and-algorithm/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/articles/flow-and-algorithm/content.md
SHA-256: 6527900c9219ba5eca67eaba2986a017114026bb60278198a4937764a857fbaa
Migrated source text
## `beskid pckg pack` algorithm
```mermaid
flowchart TD
A[Resolve source root] --> B[detect_pack_profile]
B --> C[collect_pack_entries walk]
C --> D[apply_pack_readme]
D --> E[build_package_json]
E --> F[ZipWriter + SHA256]
F --> G[.bpk artifact]
```
1. Read `Project.proj` when present; choose `PackProfile::Library` or `Template`.
2. Walk files (respect template exclude strips via `strip_template_pack_excludes`).
3. Include `.beskid/docs/**` and package docs for library profiles.
4. Copy resolved readme to zip root `README.md` when not already present.
5. Emit `package.json` metadata (registry assigns version on publish—clients do not invent release versions in normal flows).
## Publish / upload
1. Build or locate `.bpk` bytes.
2. `PckgClient::send_multipart` to registry upload endpoint with publisher auth.
3. Server runs `PackageArtifactValidator` + documentation ingestion; failures return structured API errors mapped to `PckgError::Api`.
## Fetch into workspace
`beskid fetch` (CLI command in `beskid_cli`) uses registry URLs from `Workspace.proj` and downloads through `PckgClient`, writing materialized roots recorded later in `Project.lock`. Version resolution follows registry-assigned versions, not ad-hoc client version strings.
## Version state file
Pack may persist local version hints in `.beskid/pckg` state (`PackVersionState`) for iterative dev publishes; production CI should rely on server-assigned versions after upload.
## Error handling
All HTTP failures surface as `PckgError` with status, message, and optional body text suitable for `--verbose` CLI output.
Source Record: Verification and traceability
Authority: informative provenance
Legacy path: /platform-spec/tooling/registry-client/pckg-client-contract/articles/verification-and-traceability/
Source: site/spec-content/platform-spec/tooling/registry-client/pckg-client-contract/articles/verification-and-traceability/content.md
SHA-256: 8e3169375c1155d615be1d82d05ebef8f793ad3726d97a4431ed6aee2cc6535a
Migrated source text
## Client / compiler
| Area | Location |
| --- | --- |
| Pack profile detection | `beskid_pckg/src/pack.rs` unit tests |
| CLI wiring | `beskid_pckg/src/cli.rs` |
| Workspace fetch integration | `beskid_tests` pipeline + fetch scenarios |
## pckg server
| Area | Location |
| --- | --- |
| Artifact validation | `compiler/crates/beskid_pckg_server/tests/Unit/PackageArtifactValidatorTests.cs` |
| Publish documentation | `compiler/crates/beskid_pckg_server/tests/Unit/PackagePublishDocumentationTests.cs` |
| Workspace publish | `compiler/crates/beskid_pckg_server/tests/Integration/WorkspacePublishIntegrationTests.cs` |
| Manifest metadata | `compiler/crates/beskid_pckg_server/tests/Unit/PackageManifestMetadataReaderTests.cs` |
## Traceability
| Contract | Verification |
| --- | --- |
| `README.md` zip entry | Pack tests + server doc browser smoke |
| `api.json` primary docs model | Publish documentation tests |
| Template `packageKind` | `detect_pack_profile` + server template validators |
| Registry-assigned versions | Server API integration (no client-side version override in CI publish) |
Spec edits **must** stay aligned with `PackageManifestMetadata` and workspace provisioning changes in `compiler/crates/beskid_pckg_server/Services/Workspace/`.