4. Reserved fields

  • $schema

    • Type: string (URI)
    • Cardinality: REQUIRED for YAML and JSON encodings, exactly one. Permitted but redundant in TOML, where it MUST be written as the quoted key "$schema": $ is not a TOML bare-key character, so $schema = … is a syntax error.
    • Constraint: when present, MUST equal https://projectfile.org/schema/v1.json for documents conforming to version 1 of this specification.
  • spec_version

    • Type: string
    • Cardinality: REQUIRED for TOML encoding, exactly one. OPTIONAL in YAML and JSON, where $schema already carries the version.
    • Constraint: MUST equal "1" for documents conforming to version 1 of this specification.

A document MUST satisfy the discriminator rule of §3.4 in its encoding. Validators MUST reject a YAML or JSON document missing $schema, and MUST reject a TOML document missing spec_version, even when it carries "$schema".

  • Type: string
  • Cardinality: OPTIONAL, at most one.
  • Default: SoftwareSourceCode.
  • Values: a Schema.org type name (drawn from the Schema.org type registry). For software projects, RECOMMENDED values are SoftwareSourceCode (the default — source code repositories, libraries), SoftwareApplication (end user applications), and WebApplication. Other Schema.org types MAY be used if justified by the project. The vocabulary is intentionally open: consumers MUST NOT reject a document for using an unrecognized type, and the JSON Schema does not constrain kind to a closed enum.
  • Type: mapping
  • Cardinality: REQUIRED, exactly one.
  • Subkeys:
Key Type Cardinality Description
namespace string REQUIRED Reverse-DNS identifier of the owning entity. MUST match ^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$. Example: com.acme.
name string REQUIRED Project name. MUST match ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ (DNS-label shape).
version string OPTIONAL Project version, in the project’s own scheme. A SemVer 2.0.0 string per SemVer is RECOMMENDED; CalVer (24.04), two-part (16.2) and other native schemes are valid. Producers SHOULD treat this field as advisory; the authoritative version of an artifact remains its source-control tag or package-registry record.
title localized-string OPTIONAL Human-readable title. See Section 7 for the localized-string type.
summary localized-string OPTIONAL Single-sentence headline. SHOULD be ≤ 120 characters and SHOULD NOT contain line breaks. Distinct from description: the summary is the one-liner shown by package indices, app stores, and search results; the description is the longer prose.
description localized-string OPTIONAL Longer human-readable description. May be multi-paragraph.
created date OPTIONAL ISO-8601 calendar date of project inception. The interpretation of “inception” is producer-defined; conventional choices include the date of first commit, first publication, or directory creation.
released date OPTIONAL ISO-8601 calendar date on which the version named in version was released. Producers SHOULD update this field together with version. Consumers generating CITATION.cff or AppStream release entries SHOULD use this field for date-released.
modified date OPTIONAL ISO-8601 calendar date of the last meaningful change to the project. Conventional choices include the date of the most recent non-trivial commit or the most recent release, whichever the producer considers more representative.
visibility string OPTIONAL Whether the project is meant for public distribution. public when the producer intends it, private when it does not. Default private; see “Absence means private” below. A localized-string is NOT a valid value.

visibility is OPTIONAL and its absence is private: a consumer MUST resolve a document that declares neither value to private. The two classes of consumer are bound differently, because deriving a local value and altering remote state are not the same risk.

  • A consumer writing a “do not publish” marker into a local manifest — npm private, a Private :: Do Not Upload classifier, Cargo publish = false — MUST write it for private, which includes every document that omits the field. Such a marker is scoped to the artifact the document declares: a consumer projects visibility into a manifest for an artifact only when the document declares that artifact, because private in a package manifest guards THAT PACKAGE from publication and is not by itself a statement about the project. A manifest the document declares no artifact for is left untouched.
  • A consumer publishing through a pipeline the document already declares — a registry artifact under org.projectfile.artifacts, a release tag — MUST skip it for private, which includes every undeclared document.
  • A consumer changing a remote resource’s visibility — creating a repository, or making an existing one public or private — MUST act only on a declared value, in either direction. The default is a derivation rule, not a licence to alter state the document does not describe.

public and private are closed. A consumer MUST reject any other value rather than map it to a default, and MUST NOT treat the field as a localized-string (Section 7).

A consumer emitting version into a format that requires SemVer (npm package.json, Cargo Cargo.toml) MUST NOT write a non-SemVer value there; it SHOULD skip the field and say so.

Date fields — created, released, modified here, and from/to in §4.5.1 and §4.5a — are YYYY-MM-DD strings in every encoding, so producers MUST quote them. An unquoted 2026-04-22 parses as a native date in TOML and as a timestamp in YAML, not as a string, and a validator evaluating the parsed tree rejects it.

The project’s coordinates — the (namespace, name) pair — uniquely identify a project within the ecosystem. Namespaces are claims, not proofs of ownership: nothing in this specification verifies that a producer controls the domain whose reverse form it uses. Consumers MAY use the coordinates as a deduplication key under that understanding (see Section 8).

Project URLs are not part of identity — they live in two dedicated top-level keys: repositories (§4.3a) for source-control endpoints, which carry structural attributes (branch, path, role) the rest of the URL vocabulary does not need; and links (§4.10) for every navigational URL with a type discriminator and an open vocabulary.

  • Type: sequence of mapping
  • Cardinality: OPTIONAL.
  • Item shape:
Key Type Cardinality Description
url URI REQUIRED Clone endpoint. An includes fragment MAY omit it on its origin entry, which merges into the including document’s (§4.9). Any scheme Git accepts: https://, git@host:path (SSH), git+ssh://, git://, etc. The spec does NOT require an http-style scheme here — repositories[] carries the truthful clone endpoint, not the human-facing forge page.
type string OPTIONAL VCS kind: git (default), hg, svn, fossil, bzr. Open vocabulary.
path string OPTIONAL Sub-directory inside the repository tree where this project lives. Used by monorepos.
branch string OPTIONAL Default branch on the remote. Producers SHOULD record this when known. Consumers MAY read it to tell a default-branch push from any other — a CI lowering distinguishing release builds from branch previews does exactly that — and, when it is absent, SHOULD fall back to a conventional default rather than fail.
role string OPTIONAL Open vocabulary; recommended values: origin, mirror, archive. The value origin is reserved: it marks the canonical entry. Exactly one entry MUST carry role = origin when the sequence has more than one entry, even when no entry carries a role at all; a sole entry is implicitly origin.
issues boolean OPTIONAL Marks the repository whose forge issue tracker is the project’s canonical bug tracker. At most one entry MAY carry issues = true. When present, consumers inferring issue-tracker URLs (the links[type=bugs] derivation described in §4.10) MUST emit a link only for this entry, even when other entries also point at forge hosts that provide issue trackers. When absent on all entries, the entry with role = origin (or the sole entry) is used.
packages boolean OPTIONAL Marks the repository that package registries name as the project’s source. At most one entry MAY carry packages = true. When present, consumers writing a package manifest’s repository field (npm repository, a registry’s source link) MUST use this entry. When absent on all entries, the entry with role = origin (or the sole entry) is used.

Consumers needing “the” clone URL (badges, the one shown in CITATION.cff repository-code) MUST pick the entry with role = origin, or the sole entry; package indices pick the entry with packages = true first. When the picked entry’s URL is not http(s) and the consumer needs an http-style URL (e.g., to emit a CITATION.cff repository-code field or a <url> element that browsers can follow), the consumer SHOULD either (a) look up links[type=source-code] (§4.10) for the matching forge page, or (b) derive an http URL from the clone URL using the consumer’s own forge-host knowledge.

The forge landing page (the human-facing project page rendered by GitHub, GitLab, Forgejo, Gitea, sourcehut, etc.) is NOT a value in repositories[]. It is a links[] entry with type = "source-code" (§4.10) — distinct from the clone endpoint even when both are http URLs.

  • Type: mapping
  • Cardinality: OPTIONAL, at most one.
  • Subkeys:
Key Type Cardinality Description
spdx string REQUIRED if license is present SPDX license expression per SPDX-EXPR. Single identifiers like MIT are valid expressions. Compound expressions: MIT OR Apache-2.0, (GPL-2.0-only WITH Classpath-exception-2.0).
covers string OPTIONAL What corpus the SPDX expression applies to. Values: project (default) — applies to the entire source tree; additions — applies only to original work and packaging contributed in this repository, while bundled or wrapped upstream software retains its own licensing. See “Scope” below.
file string (path) or sequence of string (path) OPTIONAL Relative path(s) to license file(s) that ship alongside the document. A single string identifies one file; a sequence enumerates several (e.g., dual-licensed projects with both LICENSE-MIT and LICENSE-APACHE, or REUSE-style LICENSES/*.txt directories). Each entry MUST resolve per the path rules in Section 8.

Copyright holders are NOT carried under license. They are assembled by consumers from the top-level copyright block and the per-person from / to fields under people (entries whose roles contains copyright). This separation lets multiple consumers — LICENSE generators, SBOM tools, REUSE-style per-file headers — derive the same copyright lines from one source of truth.

The covers subkey resolves an ambiguity in earlier drafts: an SPDX expression alone does not say which corpus it applies to. For an original-authorship project the answer is “everything in the source tree,” but for a distribution, container packaging repository, mirror, or any project that bundles or wraps upstream software, the SPDX expression typically describes only what the project’s own contributors authored.

  • covers: project (default) — the SPDX expression applies to the entire source tree. Equivalent to omitting covers. Appropriate for projects where contributors are the original authors of substantially all source.
  • covers: additions — the SPDX expression applies only to original work and packaging contributed in this repository (e.g., Dockerfiles, build scripts, entrypoints, configuration glue, and any framework code authored alongside it). Bundled or wrapped upstream software retains its own licensing and is not relicensed by this declaration. Producers using covers: additions SHOULD enumerate per-artifact upstream licenses out-of-band (REUSE-style LICENSES/ directory, populated license.file sequence, vendored NOTICE / THIRD-PARTY files, or similar) and SHOULD make the boundary explicit in identity.description.

Consumers generating SBOMs, attribution reports, or compliance metadata MUST treat covers: additions as a signal that license.spdx is not authoritative for the full image or distribution and MUST seek upstream license metadata from the bundled artifacts themselves.

Omitting the license field entirely signals that the licensing of the project is not declared in the document; it does not imply “no license” (which is a distinct legal concept) or public domain. Projects intending “no license granted” SHOULD state so using SPDX’s LicenseRef-* mechanism or the NOASSERTION identifier where appropriate.

  • Type: mapping
  • Cardinality: OPTIONAL, at most one.
  • Subkeys:
Key Type Cardinality Description
year integer OPTIONAL Default copyright year, applied when a person or entity with the copyright role lacks a from date. A single year — date ranges per holder are derived from the per-person from / to fields under §4.5.

Copyright is conceptually separate from license: a project’s SPDX expression governs how downstream users may use the work, while the copyright lines record who holds rights and for which years. Earlier drafts collapsed both under [license] (the holders field), forcing every consumer to re-implement the parse of "Copyright YYYY[-YYYY] Name <email>" strings. The split puts the rights-holder data in one place and lets multiple tools — LICENSE generators, SBOM emitters, REUSE-style per-file headers — derive identical copyright statements from one source of truth.

Consumers assembling copyright lines:

  1. Filter both people and organizations to entries whose roles (§4.5.3) contains copyright.
  2. For each such entry, format "Copyright {from}[-{to}] {flat-name}[ <{email}>]". from and to are taken from the per-entry fields (§4.5.1 for people, §4.5a for organizations); when from is absent, fall back to [copyright].year; when to is absent, the line ends with no upper bound (representing “present”). The flat name is synthesized per §4.5.2 for people; organizations use name directly.
  3. Emit the resulting lines in document order across both lists.

A document MAY carry [copyright] without any copyright-role people (e.g., when only the year is recorded and the holder is implied by the project namespace), and MAY carry copyright-role people without a top-level [copyright] block (every such person supplies its own from). Producers SHOULD set at least one of the two so downstream consumers can render a meaningful copyright line.

  • Type: sequence of mapping
  • Cardinality: OPTIONAL.
  • Item shape:
Key Type Cardinality Description
name localized-string REQUIRED The upstream software’s own name, as its authors spell it. Example: PostgreSQL.
url URI OPTIONAL The upstream project’s landing page.
version string OPTIONAL The upstream version or series this project ships. MAY be a §3.8 reference to the field that pins it.
license string OPTIONAL SPDX license expression per SPDX-EXPR under which the upstream is distributed.
authors localized-string OPTIONAL Who wrote the upstream: one author or a collective name, as the upstream credits itself. Generators render it as a “Thanks to” line.
relation string OPTIONAL Open vocabulary; recommended values: packages (default) — the project builds or ships the upstream unmodified; fork — the project carries a modified copy of its source.

upstream names the third-party software a distribution, container packaging repository or fork exists to deliver. It pairs with license.covers (§4.4): under covers: additions the project’s own SPDX expression applies to its packaging only, and each upstream entry’s license states what applies to that upstream. Consumers presenting licensing to a reader MUST keep the two apart — the project’s license never covers an upstream entry, and an entry with no license is reported as “its own license”, not as the project’s. A project whose upstream entries are all relation: packages SHOULD declare covers: additions.

upstream is not a links[] entry: a link is navigation, while an upstream entry is a statement about what the project ships and under which terms.

  • Type: sequence of mapping
  • Cardinality: OPTIONAL.
  • Item shape: each entry is a person. Entities (organizations, projects, teams) go in the separate organizations key (§4.5a).
Key Type Cardinality Description
family-names string REQUIRED Family name(s) / surname(s). Multi-part surnames (Iberian García Márquez) go here as a single string.
given-names string OPTIONAL Given name(s) / first name(s).
name-particle string OPTIONAL Particle between given and family names. Examples: van, van der, de, von.
name-suffix string OPTIONAL Generational or honorific suffix. Examples: Jr., III, PhD.
alias string OPTIONAL Short handle, nickname, or username (e.g. a GitHub login).
display-name string OPTIONAL Producer-supplied flat name. When emitting to single-name formats (npm author, gemspec authors[], AppStream <developer_name>), consumers MUST prefer this value over any synthesized form. See §4.5.2 for the synthesis rule consumers SHOULD apply when display-name is absent.
email string OPTIONAL Contact email, per RFC 5322.
url URI OPTIONAL Homepage. Sync programs emitting CITATION.cff map this to website.
handles string → handle map OPTIONAL Platform handles/usernames. Open-vocabulary map from platform name to the handle. Each value: string, array of strings, or map of strings. Examples: signal: 'damianbuho.57', mastodon: 'mastodon.social/@user', gpg-key: { messaging: 'B64C122E…', code: '87B15E…' }, github: ['user', 'user-bot'].
affiliation string OPTIONAL Institutional affiliation (employer, university, research group). Carried into CITATION.cff and codemeta Person.affiliation.
orcid string OPTIONAL ORCID ID, per https://orcid.org/.
from ISO date string OPTIONAL First date this person was associated with the project, in YYYY-MM-DD form. General-purpose lifecycle metadata; consumers assembling copyright lines (§4.4a) use the year portion when the person’s roles contains copyright.
to ISO date string OPTIONAL Last date this person was associated with the project, in YYYY-MM-DD form. Absent means “present” — the person is still active on the project. For copyright-role entries, an absent to produces an open-ended copyright line.
pronouns string OPTIONAL Personal pronouns (e.g. she/her, he/him, they/them). Carried into human-facing outputs where pronoun display is appropriate.
roles sequence of string OPTIONAL See §4.5.3.

4.5.2 Flattening a person to a single-name string

Section titled “4.5.2 Flattening a person to a single-name string”

Consumers emitting to formats that carry only a single name string (npm author, gemspec authors[], AppStream <developer_name>, Maven POM <developer><name>) MUST use the person’s display-name when present. When display-name is absent, consumers SHOULD synthesize a flat name by concatenating, in order: given-names, name-particle, family-names, then a comma-space and name-suffix. Empty parts are skipped; remaining parts are joined with single spaces.

This default produces correct output for Western names (Alice Example, Ludwig van Beethoven, Martin Luther King, Jr.) and Iberian names with multi-part family-names (Gabriel García Márquez). It is not correct for cultures that order family name first (Hungarian, East Asian written in native order, Vietnamese), nor for mononyms with culturally significant ordering. Producers SHOULD set display-name explicitly whenever the default rule would misorder, mistransliterate, or otherwise misrepresent the name. The spec makes no attempt to encode locale-driven name-ordering rules; that responsibility lies with the producer who knows the person’s preferred presentation.

roles is a sequence of strings recording the roles this person or organization holds on the project. Recommended values: author, maintainer, founder, contributor, reviewer, funder, packager, security, community, copyright. The list is free-form; consumers MAY filter by role. Absent means unspecified. The vocabulary applies to both people and organizations (§4.5a); consumers filtering by role SHOULD iterate both lists. The packager role identifies a person or organization who assembles, distributes, or containerizes upstream software; it is distinct from author and the two MAY co-occur on a single entry. The security role identifies a member of the project’s security response team. A person entry with roles: ["security"] is a named individual on that team; an organization entry with roles: ["security"] represents the team itself (its email is the team inbox, its url is the team contact page). Consumers generating a SECURITY.md SHOULD filter by this role to populate the contact section. The community role identifies a person or organization responsible for community management and Code of Conduct enforcement. Consumers generating a CODE_OF_CONDUCT.md SHOULD filter by this role to populate the enforcement-contact section. The copyright role marks the person or organization as a rights-holder whose name appears on the assembled copyright line (§4.4a); the per-entry from / to fields supply the date range, falling back to [copyright].year when from is absent. For non-copyright-role entries, from and to remain valid historical metadata but do not contribute to copyright statements. roles is a projectfile extension over CITATION.cff, which encodes the equivalent distinction by splitting authors, maintainers, and contacts into separate top-level sequences. When a consumer needs to pick a single contact for a role-targeted artefact (e.g. SECURITY.md, CODE_OF_CONDUCT.md) and no entry carries the artefact-specific role, it SHOULD fall back to any person or organization with roles: ["maintainer"], then to any person or organization with an email address.

The people field carries persons only. Organizations and other non-person actors go in organizations (§4.5a). A consumer needing only current maintainers SHOULD filter both lists by "maintainer" in roles. A consumer building a citation record SHOULD filter by "author" in roles. A person or organization SHOULD appear once even when holding multiple roles.

When migrating legacy inputs that carry only a single flat name (npm author, git config user.name, gemspec authors[]), producers SHOULD split the string with the heuristic: the last whitespace-separated token becomes family-names; the rest becomes given-names. A single-token name maps to family-names only (legal for mononymous people). A name in the comma form Family, Given MUST be split on the comma, with the part before becoming family-names and the part after becoming given-names. When the producer cannot confidently split, it SHOULD set display-name to the original string and leave family-names as the best-effort guess so §4.5.2 round-trips losslessly. When the source clearly identifies an organization (npm author containing an Inc./LLC/Foundation/Group marker, or a CFF - name: entity), the producer SHOULD emit the entry under organizations instead.

The shape borrows from CITATION.cff so citation metadata can be derived mechanically. CFF authors merges both people and organizations filtered by role. projectfile does not mirror CFF’s full contact-field set (address, city, country, region, post-code, tel, fax, location, date-start, date-end); producers needing those fields SHOULD carry them under an extension namespace (e.g., org.citation-cff).

  • Type: sequence of mapping
  • Cardinality: OPTIONAL.
  • Item shape: each entry is an organization or other non-person entity (company, project, team, research group).
Key Type Cardinality Description
name string REQUIRED Organization or entity name.
alias string OPTIONAL Short handle.
email string OPTIONAL Contact email, per RFC 5322.
url URI OPTIONAL Homepage. Sync programs emitting CITATION.cff map this to website.
handles string → handle map OPTIONAL Platform handles and usernames. Same open-vocabulary map value shapes (string / array / map) as the person field of the same name.
orcid string OPTIONAL ORCID ID; permitted on entities per CITATION.cff.
from ISO date string OPTIONAL First date this organization was associated with the project, in YYYY-MM-DD form. Same semantics as the person field of the same name.
to ISO date string OPTIONAL Last date this organization was associated with the project, in YYYY-MM-DD form. Absent means “present”.
roles sequence of string OPTIONAL See §4.5.3.
  • Type: sequence of string
  • Cardinality: OPTIONAL.
  • Values: lowercase tags for discovery. Each tag MUST match ^[a-z0-9]([a-z0-9-]| [a-z0-9])*(?::[a-z0-9]([a-z0-9-]| [a-z0-9])*)?$. Spaces are permitted between words (e.g. open source); consecutive or trailing spaces are not.

A keyword tag has one of two shapes:

  • Bare — logging, graph-database, static-site-generator, open source. Equivalent to package.json keywords or GitHub topics: free-form thematic tags. Consumers MAY use them for indexing, search ranking, or display.
  • Namespaced — prefix:value, where prefix and value each match the bare-tag pattern (spaces allowed in the value segment). Used to attach lightweight, classifier-style metadata without growing a structured categories field. Recommended (non-controlled) prefixes:
    • topic: — subject-matter classification (topic:logging, topic:cryptography, topic:web-framework).
    • audience: — intended consumer (audience:developer, audience:scientist, audience:end-user).
    • status: — maturity (status:alpha, status:beta, status:stable, status:deprecated).
    • framework: — primary framework (framework:django, framework:rails).

Producers MAY use other prefixes; consumers MUST NOT reject documents for using them, and SHOULD treat unknown prefixes as opaque tags. Tools mapping from PyPI trove classifiers, Cargo categories, or AppStream <categories> SHOULD emit namespaced tags rather than collapsing into bare keywords.

The technologies field (Section 4.7) carries the technological taxonomy; keywords carries the thematic and classifier one.

  • Type: sequence of string
  • Cardinality: OPTIONAL.
  • Values: lowercase tags identifying technologies relevant to building, running, or consuming the project.

The technologies field is a flat set of tags. Tags typically identify languages (python, rust), runtimes (node, jvm), frameworks (laravel, nextjs), data systems (postgres, redis), and platforms (docker, wasm). Consumers MAY use technologies to generate .gitignore, select linters, scaffold CI pipelines, or present the project in search results.

The recommended vocabulary is published in spec/technologies.yaml in the specification repository. The vocabulary is non-normative: producers SHOULD prefer recommended tags so cooperating consumers behave consistently, but consumers MUST NOT reject a document for using a tag outside the recommended vocabulary. A consumer encountering an unknown tag MAY ignore it.

Producers needing a tag broadly applicable to other projects SHOULD contribute it to the recommended vocabulary by pull request (no gate-keeping; one-line rationale in the commit message). Producers with project-private taxonomy needs SHOULD use an extension namespace (e.g., com.example.technologies) instead of inflating the recommended vocabulary.

Each entry in the recommended vocabulary MAY carry an optional use-cases list of advisory tokens (e.g. gitignore, dockerignore, editorconfig) hinting at generation features a tag enables. These are non-normative and purely advisory: consumers MAY use them to offer such features, MAY ignore them, and MUST NOT reject a document on their basis. They live on the vocabulary entry, not in the projectfile document.

  • Type: mapping
  • Cardinality: OPTIONAL, at most one.
  • Subkeys:
Key Type Cardinality Description
browsers sequence of string OR mapping of string → sequence of string OPTIONAL Browser targets as Browserslist query strings. A flat sequence applies to all environments and generates a flat .browserslistrc. A mapping of lowercase environment names to query-string sequences generates per-environment [section] headers; environment names MUST match ^[a-z][a-z0-9-]*$. Absent = unconstrained.
runtime mapping of string → string OPTIONAL Required runtime versions. Keys are lowercase identifiers (typically matching a technology tag: node, python, php, ruby, go, rust, java, dotnet). Values are version ranges in SemVer 2.0.0 syntax per [SemVer]: ">=8.5", "^3.12", "~1.0".

requirements carries runtime-targeted constraints: which browser targets and runtime versions the project consumes. Platform targeting (operating system, CPU architecture) is a build/distribution concern rather than a runtime requirement, so it lives in the org.projectfile.operating-system and org.projectfile.architecture extension fields (§4.8a), not here.

browsers uses Browserslist query syntax. Producers MAY use any query string accepted by the Browserslist library; consumers MUST treat unknown or future query syntax as opaque and MUST NOT reject documents for using it. The production environment name carries special meaning in several bundlers (Vite, webpack, Parcel) and corresponds to the queries used when no NODE_ENV override is set. Producers SHOULD use production as the primary environment name where multi-environment entries are needed.

runtime ranges use SemVer syntax for portability across ecosystems. Producers whose ecosystem uses a non-SemVer scheme (PEP 440 epochs, Ruby ~> operator, .NET floating versions) MAY emit native syntax in addition under an extension namespace (e.g., org.python.pep440). Consumers SHOULD treat the SemVer form as advisory and consult the language-native manifest for authoritative resolution.

4.8a Platform targeting (org.projectfile.operating-system, org.projectfile.architecture)

Section titled “4.8a Platform targeting (org.projectfile.operating-system, org.projectfile.architecture)”

Platform targeting — which operating systems and CPU architectures a project builds or ships for — is declared under the spec’s own extension namespace, not under requirements (it is a build/distribution concern, not a runtime requirement). These two fields are the recommended, portable home for platform sets; they are extension-namespace data, so they are opaque to the core JSON Schema (§3.7).

Field Type Cardinality Description
org.projectfile.operating-system sequence of string OPTIONAL Target operating systems. Recommended values follow the OCI image-spec platform vocabulary [OCI-IMAGE-SPEC]: linux, darwin, windows, freebsd, openbsd, netbsd, illumos, solaris, aix, dragonfly, plan9, js, wasip1. Absent = unconstrained (a consumer typically assumes linux for container builds).
org.projectfile.architecture sequence of string OPTIONAL Target CPU architectures. Recommended values follow the OCI image-spec architecture vocabulary [OCI-IMAGE-SPEC]: amd64, arm64, arm, 386, riscv64, ppc64le, s390x, mips64le, loong64, wasm. Absent = unconstrained (a consumer typically assumes the build host’s architecture).

The lists are recommendations, not closed enums; consumers MUST NOT reject documents for values outside them. They mirror the vocabulary used by container tooling (Docker, Podman, Kubernetes) and Go’s GOOS/GOARCH.

When a project declares architecture without operating-system, a consumer building container images SHOULD assume linux as the operating system (the OCI default-platform convention). The cartesian product of the two lists is the project’s supported platform set; consumers lower it to their native form (e.g. linux/amd64 linux/arm64).

A consumer reading these fields SHOULD treat them as the project-wide platform set. Components of a build that target a different arch (e.g. a cross-compilation standard library bundled into every image arch, or a binary fetched for a wider arch set than the image builds) SHOULD be declared at that component, not by inflating the project-wide list — otherwise every platform-targeted component is expected to cover every declared arch.

An org.projectfile.artifacts entry MAY carry its own operating-system and architecture, which replace the project-level lists for that artifact, and an exclude list of cells ({os: darwin, arch: riscv64}) removed from their product. Every os and arch an exclude cell names MUST appear in the list it filters; a consumer MUST reject a cell that does not, because a misspelt cell would otherwise exclude nothing.

  • Type: sequence of string or mapping
  • Cardinality: OPTIONAL.
  • Values: local paths or https/http URLs referencing projectfile documents or fragments to merge before reading, or a pinned remote include (below).

Each entry is resolved in order and deep-merged into the base document: later entries win over earlier ones, and the base document always wins over all includes. The mechanism is encoding-agnostic — a YAML base may include a JSON remote fragment and vice-versa. Extension namespaces are merged recursively like any other mapping.

An entry is either:

  • A relative path — resolved against the directory containing the projectfile. Path traversal rules from Section 8 apply.
  • An HTTP(S) URL — fetched at read time. The URL’s file extension determines the encoding (.yaml, .toml, .json). Listing it is the author’s consent to that fetch (Section 8).
  • A pinned remote include — a mapping {url, sha256}, where sha256 is the lowercase hex SHA-256 of the fetched bytes. A consumer MUST reject the include when the bytes differ, a cached copy included. A plain URL follows whatever its server returns, and a branch URL changes meaning on every push; a pin, or a URL naming a tag or commit, does not.

Merge. Mappings merge key by key, recursively. Where both sides hold a scalar, or values of different types, the winner’s value replaces the other. Lists are unioned: the loser’s entries first, then the winner’s, skipping any entry deep-equal to one already present, so a value reached through two includes appears once. people and organizations merge by identity instead: entries naming the same person or organization — the same orcid, else the same email, else the same flattened name (§4.5.2) — combine into one entry, the winner’s fields taking precedence. repositories merges its origin the same way: when both sides name an origin entry (role = origin, or a sole entry with no role), the two combine into one, so a shared include can set branch for every including document without restating its url; every other entry is unioned. A merge therefore never removes a list entry; to drop an inherited one, stop including its source.

Resolution. An included document MAY declare its own includes, resolved the same way before it is merged. Its relative paths resolve against its own directory. A document fetched over HTTP(S) MAY include HTTP(S) URLs only: a consumer MUST NOT resolve a local path it names, and SHOULD warn that it skipped one. A document that includes itself, directly or transitively, is a cycle, and the consumer MUST reject it.

Failure. A failed fetch — a network error, a non-2xx status, a body that does not parse — is an error. A consumer MAY instead serve a copy it fetched earlier (on a server error, or in an offline mode), or continue without an include it cannot reach offline, and MUST report each include it served stale or left out. A consumer SHOULD bound each fetch in time and size, and SHOULD reject an HTML response, which is a login page far more often than a document. A consumer MAY cache fetched includes.

Fragments. A document reached only through includes is a fragment. It carries the same field shapes as a document but needs no discriminator and no required field, so a fragment can supply a single value such as identity.namespace. It is validated against the fragment schema, https://projectfile.org/schema/v1-fragment.json, which a fragment MAY name in $schema for editor support. Where a fragment and the base document both set a value, the base wins, identity included.

Validation. The structural contract (§3.4) applies to the merged document: a validator MUST resolve includes first and validate the result. A document that declares includes MAY therefore omit fields its includes supply, identity and its required subkeys among them, and the schema does not require them there; a validator that cannot resolve the includes checks field shapes only, and SHOULD say so.

Includes are resolved before any field is interpreted. A consumer that does not support includes MUST treat the document as valid and MUST NOT fail (peaceful cohabitation, §3.7), though its view of the document will lack the merged content.

Rationale: document composition is a core structural concern, not a tool-specific behaviour. Elevating it from an extension namespace (org.projectfile.cli.includes) to a reserved field makes it discoverable by every consumer and ensures the same merge semantics across all tooling.

  • Type: sequence of mapping
  • Cardinality: OPTIONAL.
  • Item shape:
Key Type Cardinality Description
type string REQUIRED URL category. Open vocabulary; consumers MUST NOT reject documents for unknown types. See recommended values below.
url URI REQUIRED The URL.
label localized-string OPTIONAL Human-readable display label.
preferred boolean OPTIONAL Marks the canonical entry of its type when more than one shares the same type. At most one entry per type MAY carry preferred = true.
derived boolean OPTIONAL 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.

Additional keys beyond those listed above are accepted and MUST be preserved on round-trip (peaceful cohabitation, §3.7).

links is the spec’s single typed navigational-URL surface: every URL associated with the project goes here under an open type vocabulary, with two exceptions. Source-control clone endpoints live in repositories[] (§4.3a), because they carry structural attributes — branch, path, role — that the rest of the URL vocabulary does not need. Funding configuration lives in the org.projectfile.funding extension namespace.

Cardinality and selection. A document MAY contain multiple entries with the same type (two bug trackers, two chat channels, multiple package-registry landings). When a consumer needs the URL of a given type (e.g., to emit package.json’s single bugs.url), it MUST pick: the entry with preferred = true if one exists for that type, else the sole entry of that type, else the first entry of that type in document order. Producers SHOULD set preferred = true only when more than one entry of the same type is present and the choice would otherwise be ambiguous.

Consumer fallback chains. When a consumer needs a URL of a given semantic role but the document does not carry an entry of the corresponding type, the consumer MAY fall back along a role-specific chain. The chains are consumer policy, not spec policy — emitters for package.json homepage, composer’s support, gemspec metadata, AppStream <url>, and CITATION.cff url each encode their own preferences. A reasonable default chain for a “project landing” slot is links[type=homepage] → links[type=source-code] → links[type=documentation]. Consumers SHOULD document their chains in their own changelogs; the spec does not enumerate them here.

Recommended type values (non-normative):

type Typical use
homepage Project home page (marketing site, landing page).
source-code Forge landing page (GitHub / GitLab / Forgejo / Gitea / sourcehut project page). Distinct from repositories[] (§4.3a), which carries clone endpoints.
bugs Issue tracker.
documentation Rendered project documentation site (not source).
changelog Changelog or release-notes page.
chat Real-time discussion channel (IRC, Matrix, Discord, Slack).
forum Long-form discussion forum (Discourse, mailing-list archive).
faq Frequently asked questions page.
wiki Project wiki or knowledge base.
contact General-purpose contact URL — mailto:, contact form, mailing-list signup.
translate Localization platform (Weblate, Crowdin, Transifex, Pontoon).
donation Donation or sponsorship landing page. Distinct from org.projectfile.funding, which enumerates structured platform entries; donation is a single human-facing URL.
security-policy Full security policy document.
security-report Vulnerability submission endpoint.
security-severity Project severity definitions.
security-acknowledgments Researcher hall of fame.
bug-bounty Bug bounty program landing page.
pgp-key PGP public key for encrypted disclosure.
openssf-best-practices OpenSSF Best Practices badge.
cla Contributor License Agreement signing page.
first-contribution New-contributor guide.
enforcement Code of Conduct enforcement contact page.
conduct-full-text Full Code of Conduct text.
paid-support Commercial or enterprise support offering.
status-page Service status / uptime page.
zenodo-archive Zenodo or Figshare archive landing page.
doi-landing DOI resolver or journal article.
governance Project governance document.
roadmap Public roadmap.
renovate-dashboard Renovate self-hosted dashboard.
ci CI/CD pipeline dashboard.
package-registry Package-index landing page for this project (npmjs.com, pypi.org, packagist.org, crates.io). One entry per registry where the project is published.
ai-policy AI/LLM contribution and usage policy document (governance page, AI agreement, or the generated policy file — AI_POLICY.md by default). See org.projectfile.ai for the policy declaration.
ai-blocklist Public list of contributors barred under the AI policy. Named by the publish value of org.projectfile.ai.enforcement.

Extension namespaces SHOULD use links entries for any URL with a navigational role (something a human would open) rather than declaring their own URL fields. Machine-functional endpoint URLs (registry base URLs, API endpoints, package-index URLs consumed by clients) MAY remain as structured fields in the relevant extension namespace, since they are configuration values rather than navigational links.

.onion.i2p