# Free NFC v0.1 Core Specification

Status: frozen/stable. Release date: 2026-08-25.

This document is the normative public specification for Free NFC v0.1. A third party can implement Free NFC v0.1 from this document, the schemas, the examples, and the conformance suite without reading the PHP reference implementation and without using `freenfc.tecn.pro`.

The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, MAY, and OPTIONAL are to be interpreted as described in RFC 2119 and RFC 8174.

## 1. Scope

Free NFC v0.1 defines a stable, open, media-agnostic way for a physical or digital carrier to identify content and let a compatible resolver map that identity to local, portable, or network-accessible media.

Core v0.1 includes:

- Reference syntax and parsing.
- Web Carrier and Native URI carriers.
- Manifest and Library document shapes.
- Resolver algorithm and match levels.
- Core source types.
- Player capability negotiation, source selection, and execution planning.
- Error codes and outcome contracts.
- NFC and QR semantic equivalence.
- Security, privacy, and conformance rules.

Core v0.1 does not include hosted accounts, analytics, sync, a required gateway, a required database, NFC tag writing software, service-specific adapters, signatures, or a required runtime media player.

## 2. Compatibility

An implementation MAY claim `Free NFC v0.1 Core Compatible` only after it passes every MUST test in the official non-physical conformance profiles:

- `reference`
- `manifest`
- `library`
- `resolver`
- `selection`
- `execution`

Core Compatible MUST NOT require MySQL, `freenfc.tecn.pro`, online accounts, a web server, a hosted gateway, or a runtime multimedia player.

Physical NFC and QR evidence is an optional conformance profile because automated server-side tests cannot physically scan a tag. A physical implementation SHOULD additionally provide NFC+QR equivalence evidence.

## 3. Reference Model

A Free NFC Reference has:

- `version`: currently `v1`.
- `instance_id`: an RFC 9562 UUID in lowercase canonical text form. UUID versions 1 through 8 are accepted.
- zero or more canonical identity anchors: each has `namespace` and `content_id`.
- optional `origin_hint`, derived from the Web Carrier host when available.

A Flexible Reference has only an `instance_id`. An Anchored Reference has the same instance identity plus one or more canonical identity anchors. Resolvers MUST preserve anchor order, but matching MAY succeed against any anchor.

The host or gateway in a Web Carrier is an origin hint. It MUST NOT be treated as the authority over the referenced content. Implementations MUST NOT require the original host to be online before resolving against a local library.

## 4. Reference Grammar

The frozen reference grammar is published in `reference-grammar.abnf`.

Web Carrier form:

```text
https://<host>/.well-known/freenfc/v1/i/<instance_id>[/c/<namespace>/<content_id>]*
```

Native URI form:

```text
freenfc:v1:i:<instance_id>[;c=<namespace>:<content_id>]*
```

Parsing requirements:

- Implementations MUST accept HTTPS Web Carriers with path prefix `/.well-known/freenfc/v1/i/`.
- Implementations MUST reject non-HTTPS Web Carriers for Core Web Carrier compatibility.
- Implementations MUST accept Native URIs beginning with `freenfc:v1:i:`.
- Implementations MUST reject unsupported reference versions.
- `namespace` MUST match `[a-z0-9][a-z0-9-]{0,31}`.
- `content_id` MUST be valid UTF-8, 1 to 2048 bytes, and MUST NOT contain control characters.
- Percent-encoded path or URI components MUST be valid percent encoding and decode to valid UTF-8.

## 5. Manifest Schema

The versioned JSON Schema is published at `schemas/manifest.schema.json`.

A manifest document MUST be a JSON object with:

- `version`: exactly `"0.1"`.
- `type`: non-empty string.

Optional fields:

- `instance_id`: canonical UUID.
- `canonical_identities`: array of identity objects.
- `resources`: array of resource objects.

Each resource MUST include `uri`. If `path` is present, it MUST be a safe relative path. Absolute paths, parent directory traversal, drive-letter absolute paths, and NUL bytes MUST be rejected.

Additional properties MAY appear. Core implementations MUST ignore unknown properties they do not understand unless those properties are explicitly required by a selected extension.

## 6. Library Schema

The versioned JSON Schema is published at `schemas/library.schema.json`.

A library document MUST be a JSON object with:

- `version`: exactly `"0.1"`.
- `sources`: array of source objects.

Each source MUST include:

- `id`: non-empty stable source identifier.

Each source MAY include:

- `root`: local root. `@self` means the directory containing the library file.
- `type`: default source type for contained items.
- `items`: array of library items.

Each item MAY include:

- `instance_id`
- `path`
- `uri`
- `identities`
- `canonical_identities`
- `edition_identities`
- `service_identities`
- `sources`

When `sources` is present on an item, it overrides the default source candidate produced from the parent source. Local `path` values MUST be safe relative paths. HTTP candidates MUST use `http://` or `https://`.

## 7. Source Types Core

Core v0.1 defines these source types:

- `file`: one local media file.
- `directory`: a local directory containing playable media.
- `playlist`: a local playlist file, currently M3U-style line parsing.
- `http`: an HTTP(S) media URI.

Implementations MAY support fewer source types, but they MUST report `unsupported_source` when a match exists and no candidate source type is usable.

## 8. Resolver Algorithm

A resolver receives a Reference and a Library and returns one of:

- `resolved`
- `no_match`
- `unsupported_source`
- `validation_error`

Resolution MUST evaluate match levels in this order:

1. `exact_instance`: item `instance_id` equals reference `instance_id`.
2. `exact_edition`: any reference identity matches item `edition_identities`.
3. `canonical`: any reference identity matches item `canonical_identities`; legacy `identities` MAY be treated as canonical.
4. `service`: any reference identity matches item `service_identities`.

At the first level with matches:

- If exactly one item matches, the resolver MUST return that match and normalized candidate sources.
- If more than one item matches, the resolver MUST return `no_match` with `match.ambiguous = true` and MUST NOT choose silently.
- If one item matches but no candidate uses a supported source type, the resolver MUST return `unsupported_source`.

If no level matches, the resolver MUST return `no_match` with empty candidates.

## 9. Match Levels

`exact_instance` is the strongest match and identifies the same physical/digital instance. `exact_edition` identifies a specific edition of content. `canonical` identifies the work or release across carriers. `service` identifies service-specific IDs that can help adapters, but Core v0.1 does not require service APIs.

Implementations SHOULD expose the chosen match level in resolver output.

## 10. Player Capability and Selection

A player declares:

- supported `source_types`
- supported content `kinds`
- supported `media_extensions`

Default source priority is:

```json
["file", "directory", "playlist", "http"]
```

Selection MUST:

- return `unsupported_kind` if a requested kind is not supported.
- return `no_candidates` when the resolver produced no candidates.
- return `unsupported_source` when candidates exist but none match player source capabilities.
- preserve candidate order as the tie-breaker when source priority ranks are equal.
- return `selected` with the selected candidate and policy when a usable candidate exists.

## 11. Execution Contract

Execution planning turns a selected candidate into a launch-neutral plan. Core compatibility does not require launching media.

Ready actions:

- `open_file`
- `open_uri`
- `play_sequence`

Execution outcomes:

- `ready`
- `invalid_source`
- `missing_resource`
- `unsupported_media`
- `execution_error`

Directory playback MUST traverse contained files, skip dotfiles, filter unsupported media, and order playable entries by natural lexicographic relative path. Playlist execution MUST ignore blank and comment lines, accept relative paths and HTTP(S) entries, and reject absolute or traversal entries.

## 12. Error Codes

Core implementations SHOULD use these error codes when applicable:

- `invalid_web_carrier`
- `invalid_web_scheme`
- `invalid_web_carrier_path`
- `missing_instance_id`
- `invalid_instance_id`
- `invalid_web_identity_marker`
- `incomplete_identity`
- `invalid_namespace`
- `missing_namespace`
- `missing_content_id`
- `content_id_too_long`
- `invalid_content_id`
- `invalid_namespace_encoding`
- `invalid_content_id_encoding`
- `unsupported_version`
- `invalid_native_uri`
- `invalid_native_parameter`
- `unsupported_manifest_version`
- `missing_manifest_type`
- `invalid_manifest_resources`
- `invalid_manifest_resource`
- `unsupported_library_version`
- `missing_library_sources`
- `invalid_library_source`
- `missing_source_id`
- `missing_source_type`
- `invalid_source_items`
- `invalid_library_item`
- `invalid_item_sources`
- `invalid_item_source`
- `invalid_source_uri`
- `invalid_path`
- `path_traversal`
- `missing_reference`
- `library_not_found`
- `library_read_failed`
- `invalid_json`
- `missing_source_path`
- `invalid_source`
- `playlist_read_failed`
- `execution_error`

## 13. Physical Carrier Semantics: NFC + QR

NFC and QR are carrier surfaces for the same semantic Reference.

- A QR code MAY encode a Web Carrier.
- An NFC tag MAY encode either a Web Carrier or a Native URI.
- If a Web Carrier and Native URI decode to the same `instance_id` and identity set, they MUST be treated as semantically equivalent.
- A physical scan MAY open a gateway URL for user convenience, but the gateway host remains only an origin hint.

## 14. Security and Privacy

Implementations MUST reject path traversal, absolute local paths in portable documents, malformed percent encoding, invalid UTF-8, invalid UUIDs, and unsupported reference versions.

Resolvers SHOULD prefer local sources over network sources by default. Implementations SHOULD NOT phone home during Core resolution unless the user selected a network source or enabled an extension. Implementations MUST NOT treat `freenfc.tecn.pro` or any other host as a required authority for Core resolution.

Implementations SHOULD avoid leaking library contents, scan history, player capabilities, or account identifiers to a gateway unless the user explicitly opts in.

## 15. Conformance Profiles

The official conformance suite is in `conformance/`.

Profiles:

- `reference`: parser and carrier compatibility.
- `manifest`: manifest compatibility.
- `library`: library compatibility.
- `resolver`: local resolver compatibility.
- `selection`: player selection compatibility.
- `execution`: execution plan compatibility.
- `physical`: optional NFC+QR evidence profile.

Run:

```sh
php bin/freenfc conformance
php bin/freenfc conformance --profile all
```

## 16. Reference Implementation

`freenfc.tecn.pro` and this PHP app are reference implementation surfaces. They demonstrate one implementation path and MAY host documentation, schemas, examples, and downloadable bundles. They are not normative dependencies and MUST NOT be required for third-party Core compatibility.

## 17. Extensions / Deferred to v0.2

The following are explicitly deferred and are not Core v0.1 requirements:

- WebDAV sources.
- SMB/NFS sources.
- mDNS/DNS-SD discovery.
- Spotify and Apple Music adapters.
- signatures, integrity proofs, and trust chains.
- managed accounts, sync, and analytics.
- richer media capabilities and transcoding negotiation.
- NFC writing apps and mobile provisioning tools.
- remote gateway federation.
- richer playlist formats and metadata mapping.
- paid hosting, creator accounts, and cloud library management.

## 18. Release Files

- `VERSION`
- `spec/version.json`
- `spec/reference-grammar.abnf`
- `spec/schemas/manifest.schema.json`
- `spec/schemas/library.schema.json`
- `spec/examples/`
- `conformance/README.md`
- `conformance/suite.json`
- `conformance/fixtures/`

