Package Management
This document defines the package format, registry backends, and publishing workflow for Morphir packages.
Overview
Morphir packages are distributable archives containing compiled IR and metadata. The package management system supports multiple registry backends, allowing packages to be published to and retrieved from existing infrastructure.
| Component | Description |
|---|---|
| Package Format | Standardized archive containing IR, metadata, and sources |
| Registry Backend | Pluggable storage/retrieval layer (npm, Maven, etc.) |
| Pack Command | Creates distributable package from project |
| Publish Command | Uploads package to configured registry |
Package Format
A Morphir package is a gzipped tarball (.morphir.tgz) containing the compiled distribution and metadata. The package format supports both classic (single-file) and VFS (directory tree) distribution modes.
See Distribution Structure for the complete IR format specification.
Package Contents
my-org-core-1.0.0.morphir.tgz
├── morphir.toml # Package metadata
├── .morphir-dist/ # Distribution (VFS mode, recommended)
│ ├── format.json # Distribution manifest
│ ├── pkg/
│ │ └── my-org/
│ │ └── core/
│ │ ├── module.json
│ │ ├── types/
│ │ │ └── *.type.json
│ │ └── values/
│ │ └── *.value.json
│ └── deps/ # Dependency specifications
│ └── ...
├── src/ # Source files (optional, configurable)
│ └── ...
└── CHANGELOG.md # Changelog (optional)
For simpler packages or backwards compatibility, classic mode is also supported:
my-org-core-1.0.0.morphir.tgz
├── morphir.toml # Package metadata
├── morphir-ir.json # Single-file distribution (classic mode)
├── src/ # Source files (optional)
└── CHANGELOG.md # Changelog (optional)
Distribution Types
Packages contain one of three distribution types:
| Type | Description | Use Case |
|---|---|---|
| Library | Package with full definitions | Reusable domain packages |
| Specs | Specifications only (no implementations) | SDK bindings, FFI, native types |
| Application | Definitions with named entry points | Executables, services, CLIs |
Package Metadata
The morphir.toml in a package contains distribution metadata:
[package]
name = "my-org/core"
version = "1.0.0"
description = "Core domain models for my-org"
license = "Apache-2.0"
repository = "https://github.com/my-org/morphir-packages"
[package.authors]
"Jane Doe" = "jane@my-org.com"
[package.keywords]
keywords = ["domain", "finance", "morphir"]
# Dependencies required by this package
[dependencies]
"morphir/sdk" = { git = "https://github.com/finos/morphir-sdk.git", tag = "v3.0.0" }
Distribution Manifest
The .morphir-dist/format.json identifies the distribution:
{
"formatVersion": "4.0.0",
"distribution": "Library",
"package": "my-org/core",
"version": "1.0.0",
"created": "2026-01-16T12:00:00Z"
}
Format Version: The formatVersion follows semantic versioning and corresponds to the Morphir IR specification version. Tools should check compatibility before processing.
Classic Mode (Single-File)
For backwards compatibility or simpler packages, a single morphir-ir.json can be used:
{
"formatVersion": "4.0.0",
"Library": {
"package": {
"name": "my-org/core",
"version": "1.0.0"
},
"def": {
"modules": { "...": "..." }
},
"dependencies": {
"morphir/sdk": { "...": "..." }
}
}
}
Registry Backends
Morphir supports pluggable registry backends, allowing packages to be stored in existing package infrastructure.
Backend Architecture
┌──────────────────────────────────────────────────────────────────────┐
│ Morphir CLI │
├──────────────────────────────────────────────────────────────────────┤
│ Registry Interface │
│ ┌─────────────┐ ┌───────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ npm Backend │ │GitHub Releases│ │Maven Backend│ │ (future) │ │
│ └──────┬──────┘ └───────┬───────┘ └──────┬──────┘ └──────────┘ │
└─────────┼─────────────────┼─────────────────┼────────────────────────┘
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│npm Registry│ │ GitHub │ │Maven Repo │
│ (npmjs.org)│ │ Releases │ │ (Central) │
└───────────┘ └───────────┘ └───────────┘
Backend Comparison
| Backend | Package Format | Infrastructure | Best For |
|---|---|---|---|
| npm | tar.gz | Central registry | JavaScript ecosystem, public packages |
| GitHub Releases | .morphir.tgz | Per-repository | Open source, no extra infrastructure |
| Maven | JAR | Central repository | JVM ecosystem, enterprise |
Registry Interface
/// Registry backend interface
pub type RegistryBackend {
/// Publish a package to the registry
publish: fn(package: PackageArchive, config: RegistryConfig) -> Result(PublishResult, RegistryError)
/// Fetch a package from the registry
fetch: fn(name: PackagePath, version: SemVer, config: RegistryConfig) -> Result(PackageArchive, RegistryError)
/// List available versions of a package
versions: fn(name: PackagePath, config: RegistryConfig) -> Result(List(SemVer), RegistryError)
/// Search for packages
search: fn(query: String, config: RegistryConfig) -> Result(List(PackageInfo), RegistryError)
}
npm Backend
The npm backend stores Morphir packages as npm packages, leveraging existing npm infrastructure and tooling.
Package Mapping
| Morphir | npm |
|---|---|
my-org/core | @morphir/my-org--core |
1.0.0 | 1.0.0 |
.morphir.tgz | .tgz (standard npm tarball) |
npm Package Structure
package/
├── package.json # npm metadata
├── morphir.toml # Morphir metadata
├── morphir-ir.json # Compiled IR
└── src/ # Sources (if included)
Generated package.json:
{
"name": "@morphir/my-org--core",
"version": "1.0.0",
"description": "Core domain models for my-org",
"morphir": {
"name": "my-org/core",
"formatVersion": 4
},
"files": [
"morphir.toml",
"morphir-ir.json",
"src/"
],
"keywords": ["morphir", "domain", "finance"],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/my-org/morphir-packages"
}
}
Configuration
# morphir.toml
[registry]
backend = "npm"
[registry.npm]
# npm registry URL (default: https://registry.npmjs.org)
registry = "https://registry.npmjs.org"
# Scope for published packages (default: @morphir)
scope = "@morphir"
# Package name separator (default: --)
separator = "--"
# Access level for scoped packages
access = "public" # or "restricted"
Authentication
npm authentication uses standard npm configuration:
# Login to npm registry
npm login --registry=https://registry.npmjs.org
# Or use token-based auth
npm config set //registry.npmjs.org/:_authToken=${NPM_TOKEN}
The Morphir CLI respects .npmrc for authentication.
GitHub Releases Backend
The GitHub Releases backend fetches Morphir packages from GitHub release assets. This is ideal for open source projects and organizations that want to distribute packages directly from their repositories without additional infrastructure.
Unlike npm or Maven backends which use a central registry, GitHub Releases packages are fetched directly from individual repositories. Each package specifies its source repository.
Package Discovery
Packages are discovered from release assets matching the Morphir package naming convention:
| Release Asset | Morphir Package |
|---|---|
morphir-sdk-3.0.0.morphir.tgz | morphir/sdk@3.0.0 |
my-org-core-1.0.0.morphir.tgz | my-org/core@1.0.0 |
Dependency Declaration
Each GitHub dependency specifies its source repository:
[dependencies]
# Package from finos/morphir repository
"morphir/sdk" = { github = "finos/morphir", tag = "sdk-v3.0.0" }
# Package from a different organization's repository
"acme/domain" = { github = "acme-corp/morphir-domain", tag = "v1.0.0" }
# Package from a monorepo with package-specific tags
"my-org/core" = { github = "my-org/morphir-packages", tag = "core-v1.0.0" }
"my-org/utils" = { github = "my-org/morphir-packages", tag = "utils-v2.0.0" }
# Private repository (requires authentication)
"internal/models" = { github = "my-org/internal-models", tag = "v1.5.0" }
Workspace-Level Configuration
Common settings can be configured at the workspace level:
# morphir.toml
[registry.github]
# GitHub API URL (for GitHub Enterprise)
api_url = "https://api.github.com" # default, or https://github.mycompany.com/api/v3
# Asset naming pattern (default shown)
asset_pattern = "{name}-{version}.morphir.tgz"
# Default organization for unqualified references
default_owner = "my-org"
Workspace Dependencies with GitHub
# workspace/morphir.toml
[workspace]
members = ["packages/*"]
[workspace.dependencies]
# Shared GitHub dependencies
"morphir/sdk" = { github = "finos/morphir", tag = "sdk-v3.0.0" }
"finos/morphir-json" = { github = "finos/morphir-json", tag = "v1.0.0" }
# workspace/packages/domain/morphir.toml
[dependencies]
"morphir/sdk" = { workspace = true } # Inherits github source from workspace
Authentication
For private repositories, GitHub authentication uses standard methods:
# Via environment variable
export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
# Or via gh CLI authentication
gh auth login
# For GitHub Enterprise
export GH_ENTERPRISE_TOKEN=ghp_xxxxxxxxxxxx
Checksums and Bill of Materials
For security and reproducibility, GitHub releases should include checksums and optionally a Software Bill of Materials (SBOM).
Checksum file (CHECKSUMS.txt):
sha256:abc123def456... my-org-core-1.0.0.morphir.tgz
sha256:789xyz012... my-org-utils-1.0.0.morphir.tgz
Morphir manifest (morphir-manifest.json):
{
"formatVersion": "4.0.0",
"repository": "my-org/morphir-packages",
"tag": "v1.0.0",
"created": "2026-01-16T12:00:00Z",
"packages": [
{
"name": "my-org/core",
"version": "1.0.0",
"asset": "my-org-core-1.0.0.morphir.tgz",
"checksum": "sha256:abc123def456...",
"dependencies": [
{ "name": "morphir/sdk", "version": "3.0.0" }
]
}
]
}
The CLI verifies checksums when fetching packages:
[registry.github]
# Require checksum verification (default: true)
verify_checksums = true
# Checksum file name pattern
checksum_file = "CHECKSUMS.txt"
# Optional manifest file
manifest_file = "morphir-manifest.json"
Publishing to GitHub Releases
# Pack the package (generates checksum)
morphir pack
# Generate checksums file for all packages
morphir pack --checksums dist/CHECKSUMS.txt
# Generate manifest
morphir pack --manifest dist/morphir-manifest.json
# Create a GitHub release with package and checksums
gh release create v1.0.0 \
dist/my-org-core-1.0.0.morphir.tgz \
dist/CHECKSUMS.txt \
dist/morphir-manifest.json \
--repo my-org/morphir-packages \
--title "my-org/core v1.0.0" \
--notes "Release notes"
# Or use morphir publish (generates and uploads checksums automatically)
morphir publish --backend github --repository my-org/morphir-packages --tag v1.0.0
Comparison with Git Repository Dependencies
| Aspect | Git Repository | GitHub Releases |
|---|---|---|
| Source | Full repository clone | Release asset only |
| Size | Entire repo history | Single package archive |
| Speed | Slower (clone) | Faster (direct download) |
| Versioning | Any git ref | Release tags only |
| Use case | Development, pre-release | Published releases |
For development and pre-release packages, use git repository dependencies. For published, stable releases, GitHub Releases provides a more efficient distribution mechanism.
Maven Backend
The Maven backend stores Morphir packages as JAR files in Maven repositories.
Package Mapping
| Morphir | Maven |
|---|---|
my-org/core | dev.morphir:my-org-core |
1.0.0 | 1.0.0 |
.morphir.tgz | .jar containing Morphir files |
JAR Structure
my-org-core-1.0.0.jar
├── META-INF/
│ ├── MANIFEST.MF
│ └── maven/
│ └── dev.morphir/
│ └── my-org-core/
│ ├── pom.xml
│ └── pom.properties
├── morphir.toml
├── morphir-ir.json
└── src/
Generated pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>dev.morphir</groupId>
<artifactId>my-org-core</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>my-org/core</name>
<description>Core domain models for my-org</description>
<properties>
<morphir.name>my-org/core</morphir.name>
<morphir.formatVersion>4</morphir.formatVersion>
</properties>
<dependencies>
<dependency>
<groupId>dev.morphir</groupId>
<artifactId>morphir-sdk</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
</project>
Configuration
# morphir.toml
[registry]
backend = "maven"
[registry.maven]
# Maven repository URL
repository = "https://repo1.maven.org/maven2"
# For publishing (e.g., Sonatype OSSRH)
snapshot_repository = "https://oss.sonatype.org/content/repositories/snapshots"
release_repository = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
# Group ID prefix (default: dev.morphir)
group_id = "dev.morphir"
Authentication
Maven authentication uses standard Maven settings:
<!-- ~/.m2/settings.xml -->
<settings>
<servers>
<server>
<id>ossrh</id>
<username>${env.MAVEN_USERNAME}</username>
<password>${env.MAVEN_PASSWORD}</password>
</server>
</servers>
</settings>
CLI Commands
Pack
Creates a distributable package from a project.
# Pack current project
morphir pack
# Pack specific project in workspace
morphir pack --project my-org/core
# Pack with specific output location
morphir pack --output dist/
# Pack without sources
morphir pack --no-sources
# Pack for specific backend (affects format)
morphir pack --backend npm
morphir pack --backend maven
Behavior
- Verify project builds successfully
- Validate package metadata (name, version, license)
- Compile IR to distribution format
- Create archive based on backend format
- Write to output directory
Output
$ morphir pack
Building my-org/core...
Validating package metadata...
Creating package archive...
Created: dist/my-org-core-1.0.0.morphir.tgz
Size: 45.2 KB
IR Format: v4
Includes sources: yes
Publish
Uploads a package to the configured registry.
# Publish current project
morphir publish
# Publish specific package file
morphir publish dist/my-org-core-1.0.0.morphir.tgz
# Publish to specific registry
morphir publish --registry https://npm.my-org.com
# Dry run (validate without uploading)
morphir publish --dry-run
# Publish with specific tag (npm)
morphir publish --tag beta
Behavior
- Verify package archive exists (or run pack)
- Validate credentials for registry
- Check if version already exists
- Transform package for backend (if needed)
- Upload to registry
- Verify upload success
Output
$ morphir publish
Publishing my-org/core@1.0.0 to npm...
Authenticating with registry.npmjs.org...
Uploading @morphir/my-org--core@1.0.0...
Published: https://www.npmjs.com/package/@morphir/my-org--core
Other Commands
# List published versions
morphir registry versions my-org/core
# Search for packages
morphir registry search "domain model"
# Show package info
morphir registry info my-org/core@1.0.0
# Unpublish (if supported by registry)
morphir registry unpublish my-org/core@1.0.0 --force
WIT Interface
/// Package archive
record package-archive {
/// Package name
name: package-path,
/// Package version
version: semver,
/// Archive contents (tar.gz bytes)
data: list<u8>,
/// Checksum
checksum: string,
}
/// Registry configuration
record registry-config {
/// Backend type
backend: registry-backend-type,
/// Registry URL
url: string,
/// Additional backend-specific options
options: list<tuple<string, string>>,
}
/// Backend types
enum registry-backend-type {
npm,
github,
maven,
}
/// Pack a project into a distributable package
pack: func(
project: package-path,
options: pack-options,
) -> result<package-archive, package-error>;
/// Publish a package to a registry
publish: func(
archive: package-archive,
config: registry-config,
) -> result<publish-result, registry-error>;
/// Fetch a package from a registry
fetch-package: func(
name: package-path,
version: semver,
config: registry-config,
) -> result<package-archive, registry-error>;
JSON-RPC
Pack
Request:
{
"method": "package/pack",
"params": {
"project": "my-org/core",
"options": {
"includeSources": true,
"outputDir": "dist/"
}
}
}
Response:
{
"result": {
"path": "dist/my-org-core-1.0.0.morphir.tgz",
"name": "my-org/core",
"version": "1.0.0",
"size": 46284,
"checksum": "sha256:abc123..."
}
}
Publish
Request:
{
"method": "package/publish",
"params": {
"archive": "dist/my-org-core-1.0.0.morphir.tgz",
"registry": {
"backend": "npm",
"url": "https://registry.npmjs.org"
}
}
}
Response:
{
"result": {
"name": "my-org/core",
"version": "1.0.0",
"url": "https://www.npmjs.com/package/@morphir/my-org--core",
"backend": "npm"
}
}
Configuration Reference
Project-Level
# morphir.toml
[project]
name = "my-org/core"
version = "1.0.0"
[package]
# Package metadata
description = "Core domain models"
license = "Apache-2.0"
repository = "https://github.com/my-org/core"
readme = "README.md"
changelog = "CHANGELOG.md"
# What to include in package
[package.include]
sources = true # Include src/ directory
readme = true # Include README
changelog = true # Include CHANGELOG
# Files to exclude from package
[package.exclude]
patterns = ["*.test.morphir", "internal/*"]
[registry]
# Default backend
backend = "npm"
[registry.npm]
scope = "@my-org-morphir"
access = "public"
Workspace-Level
# morphir.toml (workspace root)
[workspace]
members = ["packages/*"]
# Default registry settings for all members
[registry]
backend = "npm"
[registry.npm]
scope = "@my-org-morphir"
registry = "https://npm.pkg.github.com"
Dependency Resolution with Registries
When registry dependencies become available, the dependency system will integrate with registry backends:
[dependencies]
# Future: registry dependency
"morphir/sdk" = "3.0.0"
# Current: explicit registry
"morphir/sdk" = { version = "3.0.0", registry = "npm" }
# With specific registry URL
"internal/utils" = { version = "1.0.0", registry = { backend = "npm", url = "https://npm.internal.com" } }
Resolution will:
- Query configured registry backend for available versions
- Download package archive
- Extract to dependency cache
- Verify checksum
Best Practices
- Semantic Versioning: Follow semver for version numbers
- Meaningful Descriptions: Include clear package descriptions
- License Compliance: Always specify license
- Minimal Packages: Exclude test files and internal modules
- Changelog: Maintain a changelog for each version
- CI/CD Publishing: Automate publishing from CI pipelines
Future Backends
Additional backends may be supported in the future:
| Backend | Format | Use Case |
|---|---|---|
| OCI Registry | Container image layers | Cloud-native deployments, artifact registries |
| S3/GCS | Object storage | Private infrastructure, air-gapped environments |
| Artifactory | Universal | Enterprise artifact management |
| Morphir Registry | Native format | Dedicated Morphir ecosystem (planned) |