# pasteToPrint Inline PTDF Deep Link v1

## Status

This document defines the version 1 structured-document fragment contract.
Receiver support is implemented in `assets/js/external-import.js`. Clients
MUST NOT advertise the link as supported on the production service until that
receiver version is deployed there.

The contract is intended for code-capable AI clients that can serialize and
verify a small PTDF 1.0 document but cannot or must not create an API session.
It transfers the document without an HTTP `POST`, server-side document storage,
clipboard operation, browser extension, or account.

## Canonical form

The canonical production link is:

```text
https://www.pastetoprint.com/#ptdf={payload}
```

`payload` is created by:

1. validating a PTDF 1.0 object with the exact wire identifier
   `schemaVersion: "ptp.document/1"`;
2. serializing it as UTF-8 JSON, preferably with no insignificant whitespace;
3. encoding those exact UTF-8 bytes as RFC 4648 base64url;
4. removing all trailing `=` padding.

The payload alphabet is therefore only `A-Z`, `a-z`, `0-9`, `-`, and `_`.
Percent-encoding, standard base64 characters `+` and `/`, and `=` padding are
not canonical and MUST NOT be emitted.

The format does not use compression. A future compressed representation must
use a different fragment member and must not reinterpret `ptdf`.

## Producer example

```js
function bytesToBase64Url(bytes) {
  let binary = "";
  const chunkSize = 0x8000;

  for (let offset = 0; offset < bytes.length; offset += chunkSize) {
    binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
  }

  return btoa(binary)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/g, "");
}

const documentData = {
  schemaVersion: "ptp.document/1",
  title: "Service report",
  locale: "en",
  paper: { size: "A4", orientation: "portrait" },
  template: "work_report",
  blocks: [
    { type: "heading", level: 1, text: "Service report" },
    { type: "paragraph", text: "Inspection complete." }
  ]
};

const json = JSON.stringify(documentData);
const payload = bytesToBase64Url(new TextEncoder().encode(json));
const url = "https://www.pastetoprint.com/#ptdf=" + payload;
```

Before returning the link, a producer MUST decode the payload, parse the JSON,
and verify that it reproduces the intended PTDF document. A language model
without deterministic byte-encoding or code execution SHOULD NOT attempt to
construct this link manually. It should use the plain-text link, a PTDF file,
MCP, or an API-backed session instead.

## Receiver behavior

After the pasteToPrint application has loaded, the receiver:

1. captures the raw fragment locally;
2. immediately removes the import fragment and suppressed query selectors with
   `history.replaceState`;
3. verifies that the captured fragment contains exactly one non-empty `ptdf`
   member;
4. checks the fragment and encoded-payload limits before decoding;
5. verifies the unpadded base64url alphabet and canonical encoding;
6. decodes the base64url value to bytes;
7. checks the decoded-byte limit;
8. decodes UTF-8 with fatal error handling;
9. parses exactly one JSON value;
10. requires a JSON object with `schemaVersion: "ptp.document/1"`;
11. validates and normalizes it with the published PTDF 1.0 validator;
12. renders the existing safe import preview;
13. waits for the user to choose append, replace, or cancel.

The receiver MUST reject malformed base64url, invalid UTF-8, invalid JSON,
arrays, non-PTDF objects, unknown fields, unsupported blocks, and documents
that exceed PTDF or link limits.

Opening the link MUST NOT automatically alter the editor, print, export, or
send the document. PTDF text is data, not HTML. The renderer accepts only the
published semantic blocks and applies the same sanitization and confirmation
boundary as other structured imports.

## Import-selector precedence

For backward compatibility, the receiver uses this order:

1. `?externalImport=1` window-to-window handshake;
2. `agentImport` capability token in the query or fragment;
3. inline structured fragment `#ptdf=`;
4. inline plain-text fragment `#text=`;
5. remote JSON `?import=`;
6. legacy query `?text=`.

A higher-priority selector suppresses all lower-priority selectors. A fragment
containing duplicate `ptdf` members is invalid. Unknown fragment members do
not activate an import.

## Limits

The following limits apply:

- Portable recommendation: the complete URL SHOULD NOT exceed 8,000
  characters.
- Receiver hard limit: the complete fragment MUST NOT exceed 131,200
  characters.
- The encoded `ptdf` payload MUST NOT exceed 131,072 characters.
- The decoded UTF-8 JSON MUST NOT exceed 96,000 bytes.
- The decoded document must also satisfy every PTDF 1.0 schema limit.

The receiver MUST apply encoded limits before base64url decoding and the byte
limit before UTF-8 decoding or JSON parsing.

Producers MUST NOT truncate blocks, text, table cells, or JSON to make a link
fit. If the portable recommendation is exceeded, use a `.ptdf` file, MCP, or
an API-backed session. Inline images are technically allowed when the complete
document fits all limits, but producers SHOULD prefer files or session assets
because base64 images make links large and fragile.

## Network and privacy properties

The URL fragment is not included in the HTTP request that loads
`https://www.pastetoprint.com/`. Processing `#ptdf=` MUST NOT perform a
document-content `fetch`, create a server session, or send the decoded document
to pasteToPrint.

The application itself is still loaded using normal HTTPS `GET` requests. The
fragment can remain visible to the AI chat, chat history, browser history
before cleanup, installed extensions, screenshots, screen recording, or anyone
who receives the link.

Producers MUST NOT place passwords, API tokens, private keys, payment-card data,
or other secrets in an Inline PTDF Deep Link. Client-side analytics and error
reporting MUST NOT record `location.href`, `location.hash`, the encoded
payload, the decoded JSON, or rendered document content.

## Compatibility

Version 1 always means uncompressed, unpadded base64url of UTF-8 PTDF 1.0 JSON.
Receivers MUST NOT guess another character encoding, repair JSON, add a missing
schema version, or reinterpret arbitrary JSON as PTDF.

The fragment member `text` remains reserved for Inline Text Deep Link v1 and
must never be treated as structured content. The fragment member `ptdf`
remains reserved for this structured format.
