Skip to content

phony.json and phony.lock ​

The package manifest and lockfile, verified against phony-cli/src/pkg/* and crates/pgdl/src/package.rs. A package is a single directory with a phony.json at its root. For the model and workflow see Packages and the package CLI commands.

Manifest fields ​

FieldTypeRequiredDescription
namestringyesThe package id @scope/pkg (non-empty).
versionstringyesThe package semver (1.2.0); the resolved git commit is authoritative.
generatorsarraynoTier 1 composition generators (pure data — installing a package never runs code).
assetsobjectnoLocale contributions: lists and models (below).
dependenciesobjectnoDependency name → git source (object or shorthand).
json
{
  "name": "@acme/finance",
  "version": "0.1.0",
  "generators": [
    { "name": "@acme/finance:gross", "params": {}, "let": {}, "body": "…" }
  ],
  "assets": {
    "lists":  { "@acme/finance:currencies": { "*": ["USD","EUR","TRY"], "tr_TR": ["TRY","USD"] } },
    "models": { "@acme/finance:ticker":     { "*": "models/ticker.ngram" } }
  },
  "dependencies": {
    "@acme/colors": { "git": "https://github.com/acme/colors", "version": "^1.2" }
  }
}

generators ​

Each generator is a composition. Every generator name must be in this package's own namespace — it must start with <name>: (e.g. package @acme/finance → @acme/finance:gross). A generator outside the namespace, or without a name, is a validation error.

assets ​

Two groups, each mapping an asset name to a per-locale map:

assets.lists  = { "<asset>": { "<locale>": <list-value>, … }, … }
assets.models = { "<asset>": { "<locale>": <model-value>, … }, … }

The locale key * is the locale-independent contribution; other keys are locale tags (e.g. tr_TR). Loading is additive across a dependency closure, merged into a shared contribution index. Asset keys are intentionally looser than generator names: a package may contribute to another package's asset (the locale-contribution model), so asset keys are not namespace-restricted.

List value — one of:

  • an inline JSON array;
  • an inline JSON object (a map, for key-grouped data);
  • a path string to a .json file (array or object), relative to the package directory;
  • a remote asset { url, sha256 } whose blob holds that JSON.

Model value — one of:

  • a path string to a portable .ngram file, relative to the package directory (see the .ngram format);
  • a remote asset { url, sha256 } resolved to a store blob.

File paths must stay inside the package: an absolute path or any .. component is rejected (directory-traversal guard).

dependencies ​

Each entry maps a dependency name to a git source, in object or shorthand form.

Object form:

KeyTypeDescription
gitstring (required)The clone URL. May not begin with - (argument-injection guard).
versionstringA semver range (^1.2, >=1 <2, 1.2.3, *). Must not be v-prefixed.
refstringA branch or commit, pinned exactly. May not begin with -.
pathstringA relative subdirectory holding the package's phony.json. No .., no absolute.

version and ref are mutually exclusive, and exactly one is required.

Shorthand form — a string <host>/<path>@<spec>:

  • expands git to https://<host>/<path>;
  • <spec> is a version when it is semver-shaped (starts with a digit, v+ digit, or one of ^ ~ > < = * x) and not commit-shaped; otherwise it is a ref. A hex, commit-shaped spec (all hex and either ≥ 7 chars or containing an a–f letter) is always a ref, so a digit-leading commit like 1a2b3c4 is not misread as a version.
json
{ "dependencies": {
  "@acme/colors":  "github.com/acme/colors@2.1.0",
  "@acme/nightly": "github.com/acme/colors@main",
  "@acme/pinned":  { "git": "https://github.com/acme/x", "ref": "1a2b3c4", "path": "pkg" }
} }

Version resolution (MVS) ​

Only vX.Y.Z (or bare X.Y.Z) git tags are version candidates. Resolution uses minimal version selection: for each package it picks the lowest tag that satisfies all the version ranges gathered across the closure. Prereleases are excluded unless a range explicitly names one. A ref selector pins its branch/commit directly, bypassing version matching.

phony.lock ​

The resolved closure, pinned for reproducible installs. Consumed verbatim by install (no re-resolution) unless the caller asks to update. It holds no absolute paths — store locations are derived from the commit.

FieldTypeDescription
versionnumberLockfile format version (currently 1).
packagesarrayOne LockEntry per resolved package.

Each LockEntry:

FieldTypeDescription
namestringPackage id.
gitstringThe clone URL.
versionstringThe satisfied version (tag) or ref.
commitstringThe exact resolved commit.
checksumstringsha256:<hex> of the package directory.

Entries preserve the closure's topological order (leaves before dependents), so install (a fresh resolve) and generate (which reads the lock) load packages in the same order — keeping the (asset, locale) "later wins" tiebreak reproducible.

json
{
  "version": 1,
  "packages": [
    { "name": "@acme/colors", "git": "https://github.com/acme/colors",
      "version": "v2.1.0", "commit": "…40 hex…", "checksum": "sha256:…" }
  ]
}

Package checksum ​

checksum is sha256:<hex> over the package directory's regular files: relative paths (always /-separated) sorted lexicographically as raw bytes, each file fed to one hash stream as path · 0x00 · the byte length as an ASCII-decimal string · 0x00 · the file bytes. Symlinks are a fatal error; no filesystem metadata (mode, mtime) is hashed; empty directories are not represented.

Content-addressed store ​

The global store is shared across all projects, at $PHONY_HOME/store (else ~/.phony/store). It holds two kinds of entry:

PathContents
git/<host>/<scope>/<pkg>/<commit>A fetched git package, materialised at an exact commit.
blobs/<sha256>A remote asset blob, addressed by its lowercase-hex sha256.

Entries are installed atomically: content is written to a temp path, then renamed into place (the rename is the commit point, so a reader never sees a partial entry). An existing entry is left untouched. The store root is created user-only (0700 on Unix).

Remote (release) assets ​

Large models and lists can be fetched over https and kept out of git as content-addressed blobs. A remote asset reference is an object under assets.lists / assets.models:

KeyTypeDescription
urlstringAn https:// URL.
sha256stringLowercase-hex sha256; an optional sha256: prefix is tolerated.

Both keys must be present, so an inline data map is never mistaken for a remote asset. The blob address is the bare 64-char lowercase-hex digest; it is validated as exactly 64 hex chars before use (path-traversal guard). The bytes are fetched over https and verified against sha256 before entering the store — a mismatch is an error. --frozen forbids any fetch: a missing blob then errors instead of downloading.

json
{ "assets": {
  "models": {
    "@acme/finance:big": {
      "*": { "url": "https://cdn.example.com/big.ngram", "sha256": "sha256:9f86d0…" }
    }
  }
} }

Phony Cloud — Documentation & Specification