Swift Package Registry

LPM implements SE-0292, the Swift Package Registry standard. Swift Package Manager resolves and fetches LPM packages natively — no plugins, no workarounds.

Setup

One command configures everything — registry scope, authentication, and the SPM signing trust policy:

lpm swift-registry

This runs four steps and is fail-loud — any of them returning a non-success exit aborts the command instead of silently completing with broken signing:

  1. Registers lpmdev scope with SPM pointing to https://lpm.dev/api/swift-registry.
  2. Logs in with your LPM token (stored in keychain).
  3. Downloads LPM's CMS signing certificate to ~/.swiftpm/security/trusted-root-certs/lpm.der.
  4. Writes a scope override into ~/.swiftpm/configuration/registries.json so SPM accepts packages signed by LPM's self-signed certificate.

Prerequisites: You must be logged in (lpm login) and have Swift 5.9+ installed.

Manual Setup

If you prefer to configure manually, you have to mirror both sides — the cert in the trust folder and the scope override in registries.json:

# 1. Set registry scope
swift package-registry set --scope lpmdev https://lpm.dev/api/swift-registry

# 2. Authenticate
swift package-registry login https://lpm.dev/api/swift-registry --token YOUR_LPM_TOKEN --no-confirm

# 3. Download LPM's CMS signing certificate
mkdir -p ~/.swiftpm/security/trusted-root-certs
curl -o ~/.swiftpm/security/trusted-root-certs/lpm.der https://lpm.dev/api/swift-registry/certificate

# 4. Write the lpmdev scope override (silentAllow on untrusted-signer)
#    LPM's cert is self-signed (not a CA), so it cannot be added to SPM's
#    root trust store; the scope override tells SPM to accept the cert
#    for the lpmdev scope without requiring a CA chain.
mkdir -p ~/.swiftpm/configuration
# (`lpm swift-registry` produces this file with the right shape — manual
# editors should run the CLI once and inspect the resulting JSON, then
# replicate it on other machines.)

Installing Packages

lpm install @lpm.dev/acme.swift-logger

The CLI automatically:

  1. Fetches the package metadata (version, product name)
  2. Edits your Package.swift — adds the dependency and product to your target
  3. Runs swift package resolve

If your project has multiple targets, the CLI prompts which one to add the dependency to. Use --yes to skip prompts and pick the first target.

What it looks like in Package.swift

After lpm install, your manifest looks like:

// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "MyApp",
    platforms: [.macOS(.v12), .iOS(.v15)],
    dependencies: [
        .package(id: "lpmdev.acme-swift-logger", from: "1.0.0"),
        .package(url: "https://github.com/apple/swift-nio.git", from: "2.60.0"),
    ],
    targets: [
        .target(name: "MyApp", dependencies: [
            .product(name: "Logger", package: "lpmdev.acme-swift-logger"),
            .product(name: "NIOCore", package: "swift-nio"),
        ]),
    ]
)

You can also edit Package.swift manually — the .package(id:) syntax is standard SE-0292.

Package Identity

LPM package names map to SE-0292 identifiers:

LPM NameSE-0292 Identifier
@lpm.dev/acme.swift-loggerlpmdev.acme-swift-logger
@lpm.dev/acme.design-systemlpmdev.acme-design-system

The pattern is: lpmdev.{owner}-{package-name}. The lpmdev scope tells SPM to fetch from the LPM registry.

Package Signing

All Swift packages published to LPM are signed with a CMS detached signature (ECDSA P-256, SHA-256). Two things to be precise about — what the signature buys you, and what it doesn't:

What the current trust model gives you

  • 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 origin is the trust anchor for "this package came from LPM" — your system's CA chain authenticates the connection that carried the cert and the tarballs.

What it does NOT give you (yet)

  • Trusted signer identity. LPM's signing cert is self-signed, non-CA, and cannot be added to SPM's root trust store. lpm swift-registry writes a silentAllow scope override for the lpmdev scope into registries.json, which tells SPM to accept the signature without a chain to a system trust root. The CMS bytes are verified, but the question "is this signer who they say they are?" is anchored in HTTPS to lpm.dev, not in a cert chain.
  • Per-publisher attribution. Today the signer is the LPM registry itself, not the package author — the signature attests "this came from LPM" rather than "this came from author X". Author-specific provenance (e.g., Sigstore) is a separate, future track.

If you remove the certificate or skip step 4 of the manual setup, SPM rejects every signed LPM package as untrusted-signer. To opt out of signature checking entirely (not recommended):

swift package resolve --resolver-signing-entity-checking warn

Transitive Dependencies

LPM packages can depend on other LPM packages. SPM resolves the entire dependency chain through the registry:

MyApp
  └── @lpm.dev/acme.swift-network (LPM)
        ├── @lpm.dev/acme.swift-logger (LPM) ← resolved via registry
        └── swift-nio (GitHub)

LPM packages can also depend on regular GitHub-hosted Swift packages. SPM handles both sources in a single resolution pass.

Install vs Source Delivery

lpm installlpm add
Use forLibrary dependenciesUI components, templates
Managed bySPM's resolverYou (code copied into your project)
Updatesswift package updateRe-run lpm add
Transitive depsAutomaticManual
CustomizationUse as-isEdit freely

Rule of thumb: If you'd import it, use lpm install. If you'd modify it, use lpm add.

Publishing Swift Packages

Publishing works the same regardless of how consumers install. The CLI auto-detects Swift from Package.swift:

cd my-swift-library
lpm publish

The server automatically:

  • Extracts Package.swift metadata (targets, platforms, dependencies)
  • Creates a zip source archive for SPM
  • Signs the archive with LPM's certificate

See Publishing for the full guide.