JSON Schema (v1)

The canonical JSON Schema (Draft 2020-12) for projectfile v1. Editors with JSON Schema support pick up validation automatically when a document references the canonical URL via the #:schema magic comment (TOML) or the $schema: key (YAML/JSON).

Every schema is served verbatim at its canonical URL — the address its own $id declares. Follow a card to read or download the raw document.

{  "$schema": "https://json-schema.org/draft/2020-12/schema",  "$id": "https://projectfile.org/schema/v1.json",  "title": "projectfile v1",  "description": "Schema for the projectfile specification, version 1. See https://projectfile.org/spec/v1. Applies to the in-memory tree resulting from parsing a projectfile.toml, projectfile.yaml, or projectfile.json file. The schema enforces the structural contract (required fields, types, identifier shapes). Semantic SHOULDs and recommended vocabularies (technology tags, OS/arch values, ) are intentionally NOT enforced — see spec §3.4 and §3.7 (peaceful cohabitation).",
  "type": "object",  "$ref": "#/$defs/fragment",  "properties": { "repositories": { "items": { "required": ["url"] } } },
  "if": { "required": ["includes"] },  "else": {    "description": "A document without includes carries its own identity. One with includes MAY take identity from them; validators check the merged document (spec §4.9).",    "required": ["identity"],    "properties": { "identity": { "required": ["namespace", "name"] } }  },
  "anyOf": [    {      "description": "YAML/JSON discriminator: $schema must be present and equal the v1 URL (spec §3.4).",      "required": ["$schema"],      "properties": { "$schema": { "const": "https://projectfile.org/schema/v1.json" } }    },    {      "description": "TOML discriminator: spec_version must be present and equal '1' (spec §3.4). The schema cannot see the encoding, so validators enforce this branch for .toml files by extension; the #:schema magic comment is an editor hint and does not survive parse.",      "required": ["spec_version"]    }  ],
  "$defs": {    "fragment": {      "type": "object",      "description": "Field shapes shared by a document and an includes fragment, with nothing required (spec §4.9).",      "properties": {        "$schema": {          "enum": ["https://projectfile.org/schema/v1.json", "https://projectfile.org/schema/v1-fragment.json"],          "description": "Discriminator for YAML/JSON encodings; pins the specification version. A fragment MAY name the fragment schema instead (spec §4.9)."        },        "spec_version": {          "const": "1",          "description": "Discriminator for TOML encoding; pins the specification version."        },        "kind": {          "type": "string",          "default": "SoftwareSourceCode",          "description": "Schema.org type. Recommended: SoftwareSourceCode, SoftwareApplication, WebApplication.",          "examples": ["SoftwareSourceCode", "SoftwareApplication", "WebApplication"]        },        "identity":     { "$ref": "#/$defs/identity" },        "repositories": { "$ref": "#/$defs/repositories" },        "license":      { "$ref": "#/$defs/license" },        "copyright":    { "$ref": "#/$defs/copyright" },        "upstream":     { "$ref": "#/$defs/upstream" },        "people":       { "type": "array", "items": { "$ref": "#/$defs/person" } },        "organizations": { "type": "array", "items": { "$ref": "#/$defs/organization" } },        "keywords":     { "type": "array", "uniqueItems": true, "items": { "$ref": "#/$defs/keyword" } },        "technologies": { "type": "array", "uniqueItems": true, "items": { "$ref": "#/$defs/technology" } },        "requirements": { "$ref": "#/$defs/requirements" },        "includes":     { "$ref": "#/$defs/includes" },        "links":        { "type": "array", "items": { "$ref": "#/$defs/linkEntry" } }      },
      "patternProperties": {        "^[a-z][a-z0-9-]*$": {          "description": "Top-level single-segment key. May be (a) an unknown reserved key not defined in this revision of Section 4 — accepted and preserved (peaceful cohabitation, §3.7); or (b) the first segment of a nested-mapping extension namespace — its subtree is opaque to this schema and MUST be preserved on round-trip. Dotted-string top-level keys are rejected by additionalProperties:false."        }      },
      "additionalProperties": false    },
    "localizedString": {      "description": "BCP 47 language-tagged string map. As a shorthand, a bare string is permitted and treated as { und: <string> } (§7).",      "oneOf": [        { "type": "string" },        {          "type": "object",          "minProperties": 1,          "patternProperties": {            "^([a-zA-Z]{2,3}|und)(-[a-zA-Z0-9]+)*$": { "type": "string" }          },          "additionalProperties": false        }      ]    },
    "uri": {      "type": "string",      "format": "uri"    },
    "reverseDns": {      "type": "string",      "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"    },
    "dnsLabel": {      "type": "string",      "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"    },
    "version": {      "type": "string",      "minLength": 1,      "description": "Project version in the project's own scheme. SemVer 2.0.0 is RECOMMENDED, not required (spec §4.3).",      "examples": ["1.0.0", "2.1.3-rc.1", "24.04", "16.2", "2026.10.1"]    },
    "semverRange": {      "type": "string",      "description": "SemVer-style range expression. Advisory pattern; consumers should consult the language-native manifest for authoritative resolution.",      "pattern": "^[\\^~><=*xXvV0-9. |&()\\-,]+$",      "examples": [">=1.0.0", "^2.1", "~3.14", "1.x", ">=8.5 <9"]    },
    "relativePath": {      "type": "string",      "description": "Relative path inside project directory; MUST NOT be absolute, contain '..' segments, or include a Windows drive letter.",      "pattern": "^(?![/\\\\])(?![A-Za-z]:[\\\\/])(?!.*\\.\\.).+$"    },
    "keyword": {      "type": "string",      "description": "Bare or namespaced (prefix:value) keyword. See spec §4.6.",      "pattern": "^[a-z0-9]([a-z0-9-]| [a-z0-9])*(?::[a-z0-9]([a-z0-9-]| [a-z0-9])*)?$"    },
    "isoDate": {      "type": "string",      "description": "ISO-8601 calendar date string in YYYY-MM-DD form.",      "pattern": "^\\d{4}-\\d{2}-\\d{2}$"    },
    "identity": {      "type": "object",      "properties": {        "namespace":   { "$ref": "#/$defs/reverseDns" },        "name":        { "$ref": "#/$defs/dnsLabel" },        "version":     { "$ref": "#/$defs/version" },        "title":       { "$ref": "#/$defs/localizedString" },        "summary":     { "$ref": "#/$defs/localizedString" },        "description": { "$ref": "#/$defs/localizedString" },        "created":     { "$ref": "#/$defs/isoDate" },        "released":    { "$ref": "#/$defs/isoDate" },        "modified":    { "$ref": "#/$defs/isoDate" },        "visibility":  { "$ref": "#/$defs/visibility" }      },      "additionalProperties": true    },
    "visibility": {      "type": "string",      "enum": ["public", "private"],      "default": "private",      "description": "Whether the project is meant for public distribution. Absence resolves to private; the default governs local derivation only, never a change to remote state (spec §4.3).",      "examples": ["public", "private"]    },
    "repositories": {      "type": "array",      "minItems": 1,      "description": "Source-control clone endpoints. Each entry has a url (a fragment MAY omit it: its origin entry merges into the including document’s, spec §4.9) and optional type/path/branch; role categorizes it (origin/mirror/archive, open vocab) and the reserved value role=origin marks the canonical entry, issues=true marks the forge used for issue tracking, packages=true the one package registries name as the source. Exactly one entry MUST carry role=origin when count > 1; a sole entry is implicitly origin. At most one entry MAY have issues=true, and at most one packages=true. url accepts any scheme git accepts — https, ssh (git@host:path), git+ssh, etc. The forge landing page is NOT a repositories[] value; it goes in links[type=source-code]. See spec §4.3a.",      "items": {        "type": "object",        "properties": {          "url":     { "type": "string", "minLength": 1, "description": "Clone endpoint. Any scheme git accepts — not constrained to http(s)." },          "type":    { "type": "string", "examples": ["git", "hg", "svn", "fossil", "bzr"] },          "path":    { "type": "string" },          "branch":  { "type": "string" },          "issues":   { "type": "boolean", "description": "True for the repository whose forge issue tracker is the project's canonical bug tracker. At most one entry MAY carry issues = true. When absent on all entries, the origin entry is used." },          "packages": { "type": "boolean", "description": "True for the repository that package registries name as the project's source. At most one entry MAY carry packages = true. When absent on all entries, the origin entry is used." },          "role":    {            "type": "string",            "description": "Open vocabulary; recommended values: origin, mirror, archive. The value origin is reserved: it marks the canonical entry (exactly one when the array has more than one element; the sole element is implicitly origin).",            "examples": ["origin", "mirror", "archive"]          }        },        "additionalProperties": true      },      "oneOf": [        {          "description": "Sole entry — implicitly origin; no origin-cardinality constraint applies.",          "maxItems": 1        },        {          "description": "Two or more entries — exactly one MUST carry role = origin; at most one MAY have issues = true, and at most one packages = true.",          "minItems": 2,          "contains": {            "type": "object",            "properties": { "role": { "const": "origin" } },            "required": ["role"]          },          "minContains": 1,          "maxContains": 1,          "allOf": [            {              "contains": {                "type": "object",                "properties": { "issues": { "const": true } },                "required": ["issues"]              },              "minContains": 0,              "maxContains": 1            },            {              "contains": {                "type": "object",                "properties": { "packages": { "const": true } },                "required": ["packages"]              },              "minContains": 0,              "maxContains": 1            }          ]        }      ]    },
    "license": {      "type": "object",      "required": ["spdx"],      "properties": {        "spdx": {          "type": "string",          "description": "SPDX license expression.",          "minLength": 1        },        "covers": {          "type": "string",          "description": "What corpus the SPDX expression applies to. 'project' (default) covers the whole source tree; 'additions' covers only original/packaging work in this repository, while bundled upstream software retains its own licensing. See spec §4.4.",          "enum": ["project", "additions"],          "default": "project"        },        "file": {          "description": "Relative path(s) to license file(s). Single string or sequence of strings. Each path MUST be inside the project directory.",          "oneOf": [            { "$ref": "#/$defs/relativePath" },            {              "type": "array",              "minItems": 1,              "uniqueItems": true,              "items": { "$ref": "#/$defs/relativePath" }            }          ]        }      },      "additionalProperties": true    },
    "upstream": {      "type": "array",      "description": "Third-party software the project packages or forks. See spec §4.4b.",      "items": {        "type": "object",        "required": ["name"],        "properties": {          "name":     { "$ref": "#/$defs/localizedString" },          "url":      { "type": "string", "format": "uri" },          "version":  { "type": "string" },          "license":  { "type": "string", "description": "SPDX license expression of the upstream." },          "authors":  { "$ref": "#/$defs/localizedString" },          "relation": { "type": "string", "description": "Open vocabulary: packages (default), fork." }        }      }    },    "copyright": {      "type": "object",      "description": "Top-level copyright block. Holder identities live under `people` with the `copyright` role; per-person `from` / `to` supply the per-holder date range. See spec §4.4a.",      "properties": {        "year": {          "type": "integer",          "description": "Default copyright year, applied when a copyright-role person has no `from` date.",          "minimum": 1900,          "maximum": 9999        }      },      "additionalProperties": true    },
    "person": {      "type": "object",      "required": ["family-names"],      "description": "A person entry in `people[]`. Persons only — entities go in `organizations[]`. See spec §4.5.",      "properties": {        "family-names":  { "type": "string", "minLength": 1, "description": "Family name(s). Multi-part surnames are a single string." },        "given-names":   { "type": "string", "minLength": 1 },        "name-particle": { "type": "string", "minLength": 1, "description": "Particle, e.g. 'van', 'van der', 'de', 'von'." },        "name-suffix":   { "type": "string", "minLength": 1, "description": "Generational or honorific suffix, e.g. 'Jr.', 'III', 'PhD'." },        "alias":         { "type": "string", "minLength": 1 },        "display-name":  { "type": "string", "minLength": 1, "description": "Producer-supplied flat name. Consumers emitting to single-name formats MUST prefer this over any synthesized form. See spec §4.5.2." },        "email":         { "type": "string", "format": "email" },        "url":           { "$ref": "#/$defs/uri" },        "handles":       { "$ref": "#/$defs/handles" },        "affiliation":   { "type": "string", "minLength": 1 },        "orcid": {          "type": "string",          "pattern": "^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]$"        },        "from":  { "$ref": "#/$defs/isoDate", "description": "First date this person was associated with the project. For copyright-role entries the year contributes to assembled copyright lines (§4.4a)." },        "to":    { "$ref": "#/$defs/isoDate", "description": "Last date this person was associated with the project. Absent means 'present'." },        "pronouns": { "type": "string", "minLength": 1, "description": "Personal pronouns, e.g. 'she/her', 'he/him', 'they/them'." },        "roles": { "$ref": "#/$defs/roles" }      },      "additionalProperties": true    },
    "organization": {      "type": "object",      "required": ["name"],      "description": "An organization or non-person entity in `organizations[]`. See spec §4.5a.",      "properties": {        "name":  { "type": "string", "minLength": 1, "description": "Organization or entity name." },        "alias": { "type": "string", "minLength": 1 },        "email": { "type": "string", "format": "email" },        "url":   { "$ref": "#/$defs/uri" },        "handles": { "$ref": "#/$defs/handles" },        "orcid": {          "type": "string",          "pattern": "^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]$"        },        "from":  { "$ref": "#/$defs/isoDate", "description": "First date this organization was associated with the project. For copyright-role entries the year contributes to assembled copyright lines (§4.4a)." },        "to":    { "$ref": "#/$defs/isoDate", "description": "Last date this organization was associated with the project. Absent means 'present'." },        "roles": { "$ref": "#/$defs/roles" }      },      "additionalProperties": true    },
    "roles": {      "type": "array",      "uniqueItems": true,      "items": {        "type": "string",        "examples": ["author", "maintainer", "founder", "contributor", "reviewer", "funder", "packager", "security", "community", "copyright"]      }    },
    "handles": {      "type": "object",      "description": "Platform handles and usernames. Open-vocabulary map from platform name to the handle or identifier on that platform (e.g. 'signal', 'matrix', 'mastodon'). Each value MAY be a string, an array of strings (multiple handles on the same platform), or a map of strings (named sub-keys, e.g. a GPG key with distinct messaging/code fingerprints).",      "additionalProperties": { "$ref": "#/$defs/handleValue" }    },
    "handleValue": {      "description": "A single platform handle value. String for the common case, array of strings when a platform has multiple handles, or map of strings for named sub-keys.",      "oneOf": [        { "type": "string", "minLength": 1 },        { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true },        { "type": "object", "additionalProperties": { "type": "string", "minLength": 1 }, "minProperties": 1 }      ]    },
    "linkEntry": {      "type": "object",      "required": ["type", "url"],      "description": "A typed navigational URL. See spec §4.10.",      "properties": {        "type":  {          "type": "string",          "minLength": 1,          "description": "URL category. Open vocabulary; consumers MUST NOT reject for unknown types. See spec §4.10 for recommended values.",          "examples": [            "homepage", "source-code", "bugs", "documentation", "changelog",            "chat", "forum", "contact", "translate", "donation",            "security-policy", "security-report", "security-severity", "security-acknowledgments",            "bug-bounty", "pgp-key", "openssf-best-practices",            "cla", "first-contribution", "enforcement", "conduct-full-text",            "paid-support", "status-page", "zenodo-archive", "doi-landing",            "governance", "roadmap", "renovate-dashboard", "ci", "package-registry"          ]        },        "url":       { "$ref": "#/$defs/uri" },        "label":     { "$ref": "#/$defs/localizedString" },        "preferred": {          "type": "boolean",          "description": "Marks the canonical entry of its type when more than one entry shares the same type. At most one entry per type MAY carry preferred = true. See spec §4.10."        },        "derived": {          "type": "boolean",          "description": "True when this link was auto-generated by a producer (e.g. the projectfile CLI's derive pass). Consumers MUST treat derived and hand-written links identically; the flag exists so producers can track ownership and avoid overwriting user-edited entries. See spec §4.10."        }      },      "additionalProperties": true    },
    "technology": {      "type": "string",      "description": "Recommended values are listed in spec/technologies.yaml. The vocabulary is non-normative (§4.7) — consumers MUST NOT reject documents for tags outside the recommended list.",      "pattern": "^[a-z0-9][a-z0-9.+-]*$"    },
    "requirements": {      "type": "object",      "properties": {        "browsers": {          "description": "Browser targets as Browserslist query strings (§4.8). A flat sequence applies to all environments; a mapping of environment names to query arrays generates per-environment [section] headers in .browserslistrc.",          "oneOf": [            {              "type": "array",              "uniqueItems": true,              "items": { "type": "string", "minLength": 1 }            },            {              "type": "object",              "minProperties": 1,              "patternProperties": {                "^[a-z][a-z0-9-]*$": {                  "type": "array",                  "uniqueItems": true,                  "items": { "type": "string", "minLength": 1 }                }              },              "additionalProperties": false            }          ]        },        "runtime": {          "type": "object",          "patternProperties": {            "^[a-z][a-z0-9-]*$": { "$ref": "#/$defs/semverRange" }          },          "additionalProperties": false        }      },      "additionalProperties": true    },
    "includes": {      "type": "array",      "items": {        "oneOf": [          {            "type": "string",            "minLength": 1,            "description": "Relative path or HTTP(S) URL referencing a projectfile document to deep-merge before reading. Resolution is recursive: an included document MAY itself declare includes; relative paths resolve against the including document's directory; cycles MUST be rejected. See spec §4.9."          },          {            "type": "object",            "required": ["url", "sha256"],            "properties": {              "url": { "type": "string", "pattern": "^https?://", "description": "HTTP(S) URL of the included document." },              "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$", "description": "Lowercase hex SHA-256 of the fetched bytes. Consumers MUST reject a mismatch (spec §4.9)." }            },            "description": "A pinned remote include (spec §4.9)."          }        ]      },      "description": "Documents to deep-merge into the base document. Later entries win over earlier ones; the base document always wins. Includes resolve recursively; cycles are rejected. See spec §4.9."    }  }}

.onion.i2p