Skip to main content

Documents Pallet

pallet-pilier-documents holds the chain-wide, deduplicated table of evidence-file fingerprints a digital product passport can cite as proof — certificates, lab reports, compliance documents. It is a separate pallet from the Passport Pallet, not a section of it: the passport pallet never keeps its own file table, and only ever asks this pallet, through the DocumentsAccess trait, whether a cited fingerprint is registered.

The table is deliberately not owned by a project or scoped to it: the same certificate can back passports from several projects, so it is stored once, keyed by its own content fingerprint — exactly as the passport pallet's own head-record table deduplicates by fingerprint rather than by publisher.

What a registration proves​

The only accurate claim a registration supports is: a file with this exact content existed no later than the block it was registered in. It never proves that whatever currently sits at the file's address is unchanged — the address is controlled by whoever published the file, who can replace or remove what is served there at any time, and this pallet has no way to detect that. The fingerprint, not the address, is the proof; the address is only where a copy can currently be fetched from.

The file table​

Reading documents.files(fingerprint) returns a FileInfo:

FieldTypeMeaning
registered_byu32 (project id)The project that registered this fingerprint. Names who is responsible for the entry — it does not make the table project-owned.
storage_endpoint_idu32Which storage endpoint (from the Registry Pallet) this file's address is relative to.
pathVec<u8>, up to 256 bytesThe part of the file's address past the storage endpoint's own common address part.
content_typeVec<u8>, up to 128 bytesThe file's content type (for example, a MIME type).
registered_atblock number (u32)The block this fingerprint was first registered in.

Assembling the file's address: concatenate the storage endpoint's own address (read from registry.storageEndpoints(storage_endpoint_id) — see the Registry Pallet) with this record's own path. Neither half is enough on its own; together they are the address a copy of the file can currently be fetched from — again, not proof that it still serves the same content, only a place to look.

The fingerprint itself is 32 bytes, computed by the caller from the file's content before calling register_file; this pallet never computes it and never inspects the file itself.

Calls​

register_file(project_id, fingerprint, storage_endpoint_id, path, content_type)​

Registers a file's fingerprint. Fails with:

  • NotProjectMember — the caller is not a member of project_id (checked against the Registry Pallet; see "Who can write" below).
  • StorageEndpointNotFound — storage_endpoint_id does not name an existing storage endpoint.
  • FileDataMismatch — this fingerprint is already registered with different storage_endpoint_id, path or content_type. Registering it again with the same data is accepted as a no-op: the existing entry, including its original registered_by, is left exactly as it was.

On a genuinely new registration, charges the current DocumentPrice (see "Price" below) before writing anything, and refunds the standard transaction fee on success. A no-op re-registration, or a failure, leaves the standard fee charged as normal.

set_price(new_price)​

Changes DocumentPrice. Restricted to the council (AdminOrigin).

Price​

DocumentPrice is a single value in this pallet's own on-chain storage, charged only when register_file actually creates a new entry. Read the value currently in force yourself:

const { ApiPromise, WsProvider } = require('@polkadot/api');
const api = await ApiPromise.create({ provider: new WsProvider('wss://rpc.pilier.dev') });
console.log((await api.query.documents.documentPrice()).toHuman());

Changed only by a council vote through set_price — never by a runtime upgrade.

Who can write​

register_file checks that the caller is a member of the named project — the same project-membership mechanism the Registry Pallet uses everywhere else on this chain (a project's owner, plus any account the owner has added as a writer). A project only has anything to register once it has been granted a company registration number by the council, so in practice this pallet's write access follows the same short, council-approved list the passport pallet's does: today, only Validité.

Worked example​

The evidence file behind this page's worked passport record (see the Passport Pallet page) was registered under project 3, storage endpoint 2 (address https://example.invalid/passport-docs-example/), path certificate-1.pdf, content type application/pdf. Its fingerprint is 0x1e28ba7ac941ad264440690254d7524f0c0259cfd4c11b1711c3d38b07249d3a. Reading it back:

const { ApiPromise, WsProvider } = require('@polkadot/api');

async function main() {
const api = await ApiPromise.create({ provider: new WsProvider('wss://rpc.pilier.dev') });
const fingerprint = '0x1e28ba7ac941ad264440690254d7524f0c0259cfd4c11b1711c3d38b07249d3a';
console.log((await api.query.documents.files(fingerprint)).toHuman());
await api.disconnect();
}

main().catch(console.error);

This returns registeredBy: 3, storageEndpointId: 2, path: "certificate-1.pdf", contentType: "application/pdf", registeredAt: 891989 — assembling its address means reading storage endpoint 2 from the registry pallet and appending certificate-1.pdf to its own address.