@capsium/packager
v0.2.1
Published
Node-side .cap tooling: read/validate packages, generate manifest/routes, SHA-256 checksums, write and extract .cap archives, verify integrity.
Readme
@capsium/packager
Node-side .cap tooling for the Capsium runtime: read, write, sign,
encrypt, verify and extract packages, and resolve composite-package
dependencies from a store directory.
Status: 0.2.0 — published to npm under the
@capsiumorg (see the repo-root release docs).
Install
npm install @capsium/packagerAll classes are small and dependency-injected (file system, archive,
hash/signature providers have defaults backed by node:fs and
node:crypto).
Reading and writing
import { PackageReader, PackageWriter, IntegrityVerifier } from '@capsium/packager';
const writer = new PackageWriter();
await writer.writeCap('path/to/package-dir', 'out.cap'); // generates manifest/routes/security
const reader = new PackageReader();
const model = await reader.readCap('out.cap'); // validates; verifies signatures when declared
const report = await new IntegrityVerifier().verifyCap('out.cap'); // typed issue listPackageReader gates on two §6 features while reading:
- Signatures (§6a): when
security.jsondeclaresdigitalSignatures, the signature is verified against the embedded public key and the package is rejected withSignatureMismatchErroron mismatch (signaturePublicKeyPemoverrides the embedded key;skipSignatureVerificationopts out). - Encryption (§6b): an encrypted package (layout below) is decrypted
transparently when
decryptionKeyPemis given; without a key it is rejected withEncryptedPackageError.
Digital signatures (§6a)
import { PackageSigner } from '@capsium/packager';
const signer = new PackageSigner();
await signer.sign('path/to/package-dir', privateKeyPem, publicKeyPem);
// embeds signature.pub.pem, regenerates security.json (+ digitalSignatures),
// writes signature.sig (raw RSA-SHA256, >= 2048-bit keys enforced)
await signer.verifyDirectory('path/to/package-dir'); // throws typed errorsThe signed payload is the concatenation, in sorted package-relative path
order, of the bytes of every checksum-covered file — identical to the Ruby
gem's construction, verifiable with
openssl dgst -sha256 -verify signature.pub.pem -signature signature.sig payload.bin.
X.509 certificate PEMs are accepted in place of the public key.
Encryption (§6b)
import { PackageCipher } from '@capsium/packager';
const cipher = new PackageCipher();
await cipher.encrypt('package.cap', recipientPublicKeyPem, 'package.encrypted.cap');
await cipher.decrypt('package.encrypted.cap', recipientPrivateKeyPem, 'package.cap');Encrypted .cap layout: metadata.json (cleartext), signature.json
(cleartext envelope), package.enc (AES-256-GCM ciphertext of the inner
zip). The 32-byte DEK is wrapped with the recipient's RSA public key
(RSA-OAEP-SHA256, MGF1-SHA256); GCM IV is 12 bytes, auth tag 16 bytes.
OpenPGP is out of scope. Wrong keys and tampered ciphertext fail with
typed DecryptionErrors.
Composite packages (§4a)
import { StoreDirectory } from '@capsium/packager';
const store = new StoreDirectory(process.env.CAPSIUM_STORE ?? '.capsium-store');
const deps = await store.loadDependencies(model.metadata.dependencies ?? {});
// Map<guid, CapsiumPackage> — newest satisfying version per dependencyA store directory holds <name>-<version>.cap files plus an optional
index.json (guid → file). Unsatisfiable dependencies throw
DependencyResolutionError.
