Pentool open asset and package protocol, version 1
This document defines the interoperable data and distribution protocol introduced by Pentool v0.5 and v0.6. It is intentionally independent of any hosted Pentool service. A conforming implementation may use local folders, Git, static HTTP, object storage, mirrors, or a searchable hosted catalog.
Principles
.penfiles and.penpkgarchives are portable data, never executable code.- Asset, package, and instance identity must not depend on absolute file paths.
- Published package versions are immutable.
- Content hashes identify exact bytes; semantic versions communicate intent.
- Registries are replaceable discovery and transport services, not authorities required to render a document.
- Installed content is verified before activation.
- Documents containing instances remain renderable without a registry, package, network connection, or source library.
- Updates are explicit, inspectable, transactional, and reversible.
Identifiers
Portable identifiers are slash-separated ASCII segments. Each segment may contain
letters, digits, ., _, or -; empty, . and .. segments are prohibited.
package: open-design/icons
asset: navigation/arrow-right
full: open-design/icons/navigation/arrow-right
Package coordinates append an immutable semantic version:
open-design/icons@1.4.0
Identifiers are case-sensitive. Publishers should use lowercase identifiers. Registries must reject identifiers that collide after the case and Unicode normalization rules of a supported target filesystem.
Asset metadata
An asset is a valid .pen document with an optional top-level asset object:
{
"format": "pentool",
"version": 3,
"name": "Arrow right",
"asset": {
"schema": 1,
"id": "navigation/arrow-right",
"name": "Arrow right",
"description": "Right-facing navigation arrow",
"asset_version": "1.4.0",
"kind": "component",
"author": "Open Design",
"license": "MIT",
"tags": ["arrow", "navigation"],
"category": "icons/navigation",
"entry_page": "asset",
"bounds": { "x": 0, "y": 0, "width": 24, "height": 24 },
"properties": {}
},
"pages": []
}
version is the .pen format version. asset_version is the design asset's
semantic version. A sha256: content hash identifies an exact encoded asset.
Unknown metadata fields must be retained by editing and packaging tools.
Local libraries
A local library is a registered directory scanned recursively for metadata-bearing
.pen assets. User libraries live in the platform-standard Pentool configuration
location. Project libraries live in .pentool/libraries.json and should use paths
relative to the project.
Indexes and thumbnails are disposable caches. They must be reconstructible from source files and must not be committed as authoritative package state.
Instances
A component instance records the source library/package, stable asset ID, semantic
version, exact content hash, imported layer IDs, transform, visibility, and approved
overrides. It also retains materialized .pen content as its fallback snapshot.
Open, render, export, copy, and detach must operate from this snapshot and must not
perform network access.
Source changes may mark an instance as outdated. They do not alter the document. An update replaces the fallback only after verification and explicit acceptance.
Package manifest
A .penpkg is a deterministic ZIP archive. It contains pentool.package.json,
metadata-bearing .pen files, optional previews, and optional license/readme data.
It cannot contain scripts, executable hooks, absolute paths, escaping paths, or
filesystem links.
{
"schema": 1,
"name": "open-design/icons",
"version": "1.4.0",
"description": "Open interface icons",
"license": "MIT",
"repository": "https://example.org/open-design/icons",
"authors": ["Open Design"],
"pentool": ">=0.6.0",
"assets": {
"navigation/arrow-right": {
"path": "assets/arrow-right.pen",
"hash": "sha256:...",
"preview": "previews/arrow-right.png"
}
},
"dependencies": {
"open-design/tokens": "^2.0.0"
}
}
Archive entries are ordered lexicographically, use / separators, fixed metadata,
and deterministic compression settings. Manifest maps are ordered by key. The
package hash is SHA-256 over the complete archive bytes. Rebuilding identical
normalized input must produce identical bytes.
Registry protocol
The minimum registry is a static directory or HTTP origin:
index.json
packages/<namespace>/<name>/<version>.penpkg
index.json schema 1 maps package names and versions to artifact paths, SHA-256
hashes, and yanked status. Paths are relative to the registry root. Static registries
need only immutable GET support; publishing may use Git, filesystem operations,
an object-store API, or a separate authenticated endpoint.
Clients must verify the downloaded archive hash and every asset hash before making an installation active. Redirect, download, archive, expanded-size, file-count, parse-time, and path limits are mandatory. A failing artifact is quarantined and must not replace an installed version.
A hosted registry may add search, profiles, moderation, signatures, analytics, and mirrors. Those services must not change package bytes or be required to interpret the package format.
Immutability, yanking, and deletion
The tuple (registry, package name, version) is immutable. Re-publishing identical
bytes is idempotent; different bytes are rejected. A version can be yanked to stop
new resolution while remaining downloadable for existing lockfiles. Permanent
deletion is reserved for legal or security emergencies and must leave a tombstone.
Lockfile
pentool.lock records schema version and sorted exact resolutions:
{
"schema": 1,
"packages": [{
"name": "open-design/icons",
"version": "1.4.0",
"source": "https://registry.example/v1/",
"package_hash": "sha256:..."
}]
}
Locked and offline operations fail rather than choosing a different version.
Opening and exporting .pen documents never modify this file.
Cache and installation
Packages are cached by verified content hash. Implementations should separate download, quarantine, verified, and active states and use atomic writes plus inter-process locks. An installation becomes visible to library discovery only after all checks succeed. Cache pruning must protect active lockfile references.
Signatures and trust
Checksums are mandatory. Protocol version 1 uses an optional JSON sidecar containing an Ed25519 signature over the ASCII package hash, the signer public key, algorithm, and schema version. Registries may advertise signer keys and projects may require signatures or allow-list registries/publishers. Credentials never appear in manifests, packages, lockfiles, URLs, logs, or exported registry configuration.
Compatibility
Readers reject unsupported mandatory schema versions. Unknown fields are retained. Clients may ignore unknown optional metadata but may not silently discard it while rewriting source documents. Compatibility failures are reported before install or instance update.
Conformance
A conforming implementation must demonstrate:
- deterministic rebuilds of the same package;
- rejection of traversal, duplicate entries, hash mismatch, and changed immutable releases;
- installation and lock verification without network access from a populated cache;
- identical rendering of an instance after its source package is unavailable;
- explicit update review and rollback without changing unrelated instances.