Swift Package Registry (SE-0292)
How LPM.dev Registry implements the SE-0292 Swift Package Registry spec — identity mapping, signing, certificates.
LPM.dev Registry implements the SE-0292 Swift Package Registry API. lpm install configures Swift Package Manager (SPM) when needed, then resolves LPM.dev Registry-hosted Swift packages natively — no manual fork-as-git-dep and no separate setup command for the normal install flow.
This page is the conceptual reference. For the user-facing setup walkthrough, see Using LPM CLI with Swift.
What SE-0292 specifies
SE-0292 defines a REST API for Swift package registries that SPM can talk to in lieu of a git URL. It covers:
- Listing versions for a package (
GET /{scope}/{name}) - Fetching a version's source archive (
GET /{scope}/{name}/{version}.zip) - Fetching a version's
Package.swiftmanifest (GET /{scope}/{name}/{version}/Package.swift) - Login (
POST /login) - Optional package signing via CMS
LPM.dev Registry implements all of the above plus signature verification metadata. SPM clients see a standard SE-0292 server.
Identity mapping
LPM.dev Registry packages are named @lpm.dev/owner.pkg-name. SE-0292 scopes can't contain ., so LPM.dev Registry maps:
| LPM.dev Registry name | SE-0292 identity |
|---|---|
@lpm.dev/owner.pkg-name | lpmdev.owner_pkg-name |
@lpm.dev/myorg.swift-utils | lpmdev.myorg_swift-utils |
Rules:
- The LPM.dev Registry scope
@lpm.dev/becomes the SE-0292 scopelpmdev(no dots, no@). - The
_between owner and package name is the unambiguous separator. LPM.dev Registry forbids_in both owner and package name (enforced at the storage layer), so the boundary is always clear even when either half contains hyphens.@lpm.dev/swift-server.async-http-clientand@lpm.dev/swift.server-async-http-clientmap to two distinct identifiers (lpmdev.swift-server_async-http-clientvslpmdev.swift_server-async-http-client) — no ambiguity.
Identity translation is automatic — lpm install @lpm.dev/owner.swift-pkg writes the SE-0292 identity into Package.swift. You don't deal with the mapping by hand.
Automatic setup during install
lpm login
lpm install @lpm.dev/owner.swift-pkgBefore resolving the first Swift package in an unconfigured project, LPM CLI performs four setup steps. Scope, certificate, and signing-trust failures abort the install. Authentication setup warns when no token is available or SPM rejects the login, but LPM.dev Registry requests will return 401 until you sign in:
- Set the registry for the
lpmdevscope.swift package-registry set --scope lpmdev https://lpm.dev/api/swift-registry - Log in. Uses the LPM.dev Registry bearer token resolved through the standard LPM CLI session manager (
LPM_TOKEN→ keychain → OIDC → none). - Install the signing certificate. Downloads the CMS signing cert (DER) from
https://lpm.dev/api/swift-registry/certificateand writes it to SPM's trust store at~/.swiftpm/security/trusted-root-certs/lpm.der. The DER body is size-checked before being written; an empty / truncated / HTML-error response aborts instead of leaving a broken cert on disk. - Configure the SPM signing trust policy. Writes
~/.swiftpm/configuration/registries.jsonwith a defaultsigning.onUnsigned = "warn"+signing.onUntrustedCertificate = "warn"policy and a scope-specific override pinninglpmdevtosigning.onUntrustedCertificate = "silentAllow"— see Trust model for the rationale.
lpm swift-registry exposes this setup explicitly for repair and certificate rotation. Its --force flag re-downloads the certificate even when a valid file is present. A failed forced refresh is fatal rather than silently retaining the stale certificate.
Trust model
What the signature buys you, and what it doesn't — be precise about both:
Current posture: detached package integrity + HTTPS transport authenticity.
- Detached package integrity. SPM verifies the CMS signature against the source archive bytes on every install. A tampered tarball fails the integrity check before any code runs.
- Transport authenticity via HTTPS. The LPM.dev Registry origin is the trust anchor for "this came from LPM.dev Registry" — the system CA chain authenticates the connection that carried both the signing cert and the tarballs themselves.
Not current posture: trusted signer identity verification.
LPM.dev Registry's signing cert is self-signed, non-CA, and code-signing-only. SPM's root trust store rejects it (root certs must have basicConstraints CA=true). The silentAllow scope override in step 4 tells SPM to accept the signature without a chain to a system trust root for the lpmdev scope. The cryptographic CMS check still runs; what's bypassed is the "is this signer in my trust chain?" gate.
The signer today is LPM.dev Registry, not the package author — the signature attests "this came from LPM.dev Registry," not "this came from author X." Per-author attribution (Sigstore, transparency log, whatever shape it takes) is a deliberately separate, future track. This page will be updated when that lands; until then, treat any "signed by author X" mental model as not implemented.
Installing a Swift package
lpm install @lpm.dev/owner.swift-pkg
lpm install @lpm.dev/owner.swift-pkg@1.2.0Behind the scenes:
- LPM CLI resolves the version against the SE-0292 endpoint.
- Selects an eligible non-test target and updates
Package.swift— appends the dependency intodependencies:and wires the product into that target'stargets:array. - Triggers
swift package resolveso SPM downloads the source archive, verifies the signature, and writesPackage.resolved.
One eligible target is selected automatically. With multiple eligible targets, a normal install opens the selector; -y / --yes skips the prompt and selects the first eligible target, matching the selector's default.
Use
lpm installfor Swift, notlpm add.lpm installis the Registry-resolved SPM dependency path;lpm addis the legacy source-delivery path.
Publishing a Swift package
lpm publishDetected as a Swift package when the repository has Package.swift at its root. The publish pipeline does the standard pack, secret scan, quality pass, and upload, plus three Swift-specific steps:
- Generate the SE-0292 source archive. Wraps the pack output into a
{pkgName}-{version}/top-level directory and creates the.zipSPM expects. The wrapper is a hard SE-0292 requirement (SPM usesstripFirstLevelsemantics on the archive). - Compute and record the SHA-256 checksum. SPM verifies this on download. The hex is stored in the release metadata; the base64 form is sent in the
DigestHTTP response header on download. - Sign the package. Generates a CMS-1.0.0 detached signature (ECDSA P-256 / SHA-256) using the LPM.dev Registry signing key. The signature is base64-encoded into release metadata and surfaced in three places per release:
- The release metadata JSON (base64 CMS string)
- The
DigestHTTP response header on download - A comment block in the
Package.swiftmanifest response
Verifying signatures
SPM verifies the CMS detached signature on every package archive at install time. LPM CLI configures the certificate and signing policy during the normal install flow. Use lpm swift-registry --force only when a documented certificate rotation or local corruption requires an explicit refresh.
Two failure shapes a user can run into:
onUnsigned: warn— a Swift package without a signature surfaces as a warning, not a hard error. LPM.dev Registry-published packages are always signed, so this typically only appears for packages outside LPM.dev Registry that SPM resolves alongside.- Cert missing or out of date — SPM emits a "signer is not trusted" error. Re-run
lpm swift-registry(orlpm swift-registry --forceif you suspect a stale cert) to refresh both the cert file and the registries.json scope override.
If the cert rotates server-side, re-run lpm swift-registry --force. A failed --force re-download is fatal — lpm will not silently keep using the stale cert.
Wire format details
For the curious — the things SPM expects from the server side:
| Endpoint | Returns |
|---|---|
GET /{scope}/{name} | List of versions (JSON) |
GET /{scope}/{name}/{version} | Release metadata (JSON) — signature, checksum, dependencies |
GET /{scope}/{name}/{version}.zip | Source archive with {name}-{version}/ top level, Digest: SHA-256={base64} header |
GET /{scope}/{name}/{version}/Package.swift | Manifest with signature in a comment block |
POST /login | Auth flow configured automatically during lpm install |
GET /certificate | Public cert in DER format |
Implementation lives in the LPM.dev Registry origin server and registry worker. LPM CLI is a client of this API; it doesn't implement the server side itself.
Limitations
- Automatic and explicit Swift Registry setup configure the
lpmdevscope only. Custom-scope publishing (for example,acmefor a private LPM.dev Registry tenant) is not surfaced in LPM CLI. - Cert rotation is manual — no automatic refresh on schedule. The signing cert is long-lived; rotation events will be communicated via release notes.
- The SE-0292
--allow-insecure-httpflag is honored only forhttp://registry URLs (i.e., local dev againsthttp://localhost). The hosted LPM.dev Registry endpoint is HTTPS-only.
See also
- Using LPM CLI with Swift — setup walkthrough + publish flow
lpm swift-registry— repair and certificate-refresh referencelpm install— the right command for SPM deps- SE-0292 spec — the upstream protocol