Packages & Registry
Shape projects can be compiled into distributable .shapec package bundles containing pre-compiled bytecode, metadata, and dependency information. Packages can be shared via the Shape Package Registry at pkg.shape-lang.dev.
Building a Package
Section titled “Building a Package”From a project directory containing shape.toml:
shape buildThis compiles all .shape files in the project into a single .shapec bundle.
Options
Section titled “Options”shape build [--output <path>] [--opt-level <0-3>]| Flag | Default | Description |
|---|---|---|
--output | <name>-<version>.shapec (portable) or <name>-<version>-<arch>-<os>.shapec (host-bound native deps) | Output path for the bundle |
--opt-level | 0 | Optimization level (reserved) |
Example
Section titled “Example”$ shape buildBuilding package 'mylib' v0.1.0...Built 3 modules into mylib-0.1.0.shapec (2048 bytes)Bundle Format
Section titled “Bundle Format”.shapec files use a binary format:
| Offset | Size | Content |
|---|---|---|
| 0 | 8 bytes | Magic: SHAPEPKG |
| 8 | 4 bytes | Format version (little-endian u32) |
| 12 | variable | MessagePack-encoded payload |
The payload contains:
- Metadata: package name, version, compiler version, source hash, entry module, build timestamp, bundle kind, build host, native portability flag
- Modules: each with its module path, compiled bytecode, export names, and per-file source hash
- Dependencies: declared dependency versions from
shape.toml - Native dependency scopes: transitive
[native-dependencies]grouped by package identity
Module Path Conventions
Section titled “Module Path Conventions”File paths are converted to module paths using :: separators:
| File Path | Module Path |
|---|---|
main.shape | main |
utils/helpers.shape | utils::helpers |
utils/index.shape | utils |
index.shape files map to their parent directory name, following the convention for directory-level module entry points.
Using Bundles as Dependencies
Section titled “Using Bundles as Dependencies”Explicit Bundle Path
Section titled “Explicit Bundle Path”Point a dependency directly at a .shapec file:
[dependencies]mylib = { path = "./libs/mylib-1.0.0.shapec" }Automatic Bundle Discovery
Section titled “Automatic Bundle Discovery”When a path dependency is resolved, the resolver checks for a .shapec file alongside the directory. If dep.shapec exists next to dep/, the bundle is preferred:
project/ shape.toml # mylib = { path = "./mylib" } mylib/ shape.toml lib.shape mylib.shapec # <-- preferred over mylib/Resolution
Section titled “Resolution”Bundle dependencies resolve with version = <bundle metadata version> in the dependency resolver. The module loader automatically detects .shapec paths during dependency setup and loads their modules into the bundle resolver.
Current resolver support:
pathdependenciesgitdependencies (cached under~/.shape/cache/git/)- semver registry dependencies (index entries loaded from
SHAPE_REGISTRY_INDEXor~/.shape/registry/index, sources fromSHAPE_REGISTRY_SRCor~/.shape/registry/src) - transitive semver constraints are solved across the full dependency graph
Module Resolution with Bundles
Section titled “Module Resolution with Bundles”When a bundle is loaded as a dependency, its modules are prefixed with the dependency name:
[dependencies]mathlib = { path = "./mathlib.shapec" }A module helpers inside the bundle becomes mathlib::helpers:
from mathlib::helpers use { normalize }The full resolution order for imports:
- Extension-provided modules (highest priority)
- Bundle-provided modules
- Embedded standard library
- Filesystem (project root, module paths, stdlib path)
Source Hash Verification
Section titled “Source Hash Verification”Each bundle records SHA-256 hashes:
- Per-module hash: hash of the individual source file
- Combined hash: hash of all source files concatenated
Compiling the same source twice produces identical hashes. Modifying any file changes the combined hash, enabling staleness detection.
Package Registry
Section titled “Package Registry”The Shape Package Registry (pkg.shape-lang.dev) hosts published packages for the community.
Publishing
Section titled “Publishing”Packages are signed with Ed25519 keys. Generate a signing key if you haven’t already:
shape keys generateThen publish from a project directory:
shape publishThis builds the project, signs the bundle with your key, and uploads it to the registry. Every published version is immutable — once published, a version cannot be overwritten.
The [project] section in shape.toml should include a description for the registry listing:
[project]name = "mylib"version = "1.0.0"description = "A useful library for Shape programs"entry = "src/lib.shape"Adding Dependencies
Section titled “Adding Dependencies”Add a registry dependency:
shape add mylibThis fetches the package index, resolves the best matching version, downloads the .shapec bundle, verifies its checksum and cryptographic signature, and adds it to shape.toml:
[dependencies]mylib = "^1.0"On first use of a package from an unknown author, you’ll see a Trust-on-First-Use (TOFU) prompt showing the author’s signing key. You can choose to trust the key for this package or for all packages by that author.
Removing Dependencies
Section titled “Removing Dependencies”shape remove mylibRemoves the dependency from shape.toml.
Searching and Inspecting
Section titled “Searching and Inspecting”shape search "json parser"shape info mylibshape info shows package details including version history, required permissions, author signing key, and documentation.
Security Model
Section titled “Security Model”Every package in the registry is:
- Signed — Ed25519 signature on each module manifest, verified on download
- Content-addressed — SHA-256 checksums on bundles, verified against the index
- Permission-declared — required capabilities (filesystem, network, etc.) are visible before install
- Author-bound — signing keys are tied to authenticated registry accounts
The verification chain on shape add:
- Download sparse index entry for the package
- Resolve version via semver solver
- Download
.shapecbundle - Verify bundle SHA-256 matches index checksum
- Deserialize and verify each module manifest integrity hash
- Verify Ed25519 signature on each manifest
- Check author key against local keychain (TOFU prompt if unknown)
- Display required permissions
Credential Storage
Section titled “Credential Storage”Registry credentials are stored in ~/.shape/credentials.json (mode 0600). API tokens use the shp_tok_ prefix and are scoped to specific operations (publish, yank).
Workflow
Section titled “Workflow”Local Development
Section titled “Local Development”# In the library projectcd mylib/shape build# produces mylib-1.0.0.shapec
# In the consumer projectcd myapp/# shape.toml: mylib = { path = "../mylib/mylib-1.0.0.shapec" }shape myapp.shapeRegistry Distribution
Section titled “Registry Distribution”# Publish a librarycd mylib/shape publish
# Use it in another projectcd myapp/shape add mylibshape runNative Bundles And Cross-Platform Compatibility
Section titled “Native Bundles And Cross-Platform Compatibility”When native libraries are involved, .shapec portability depends on how [native-dependencies] are declared.
- Portable bundles (
metadata.native_portable = true) include only system-style native specs whose resolved values are not path-like. - Host-bound bundles (
metadata.native_portable = false) include anypathorvendoredprovider, or any target entry that resolves to a path-like native value.
By default:
- portable output name:
<name>-<version>.shapec - host-bound output name:
<name>-<version>-<arch>-<os>.shapec
Target-qualified vendored packages are supported in package metadata through
targets = { "os-arch[-env]" = "..." }, but bundles using vendored or path
native assets are still host-bound today. Publish one bundle per target
platform for those packages. For portable packages, a single bundle can be
reused across platforms.
Important: current bundles preserve native dependency metadata only. The native
files referenced by provider = "path" or provider = "vendored" are not
embedded into .shapec, and shape publish uploads only the bundle bytes.
Registry distribution therefore does not yet carry those native assets; they
must be shipped separately on disk if a consumer is expected to load them.
Transitive Native Requirements From Bundles
Section titled “Transitive Native Requirements From Bundles”Bundle compilation embeds transitive native requirements into
native_dependency_scopes. When another package depends on that .shapec, the
shared native resolver ingests these scopes so frozen locks still cover
transitive native requirements.
Older bundles without embedded scopes still load, but the CLI warns that transitive native locking cannot be guaranteed for those bundles.
Inspecting The Graph
Section titled “Inspecting The Graph”Use shape tree to inspect dependency resolution:
shape treeshape tree --nativeshape treeshows source/bundle dependency edges and resolved versions.shape tree --nativeadditionally prints bundled native dependency scopes and host-bound markers.