Руководство издателя
Опубликовать товары
Публикация в prodid — это два файла в вебе и одна запись в DNS. На своём домене не нужно ни регистрироваться, ни хранить ключи: домен и есть удостоверение, а изменить или отозвать опубликованное можно, изменив файлы.
Как вы будете публиковать?
Идентичность и хостинг разделены. Ваша идентичность — это домен, который вы контролируете; где на самом деле лежат документы — деталь, которую можно поменять позже, не меняя ни идентичность, ни идентификаторы, присвоенные prodid вашим товарам. Три пути, и все дают ровно одни и те же записи.
Путь 1
Вы можете отдавать файлы со своего домена.
Отдавайте документ обнаружения с домена и опубликуйте запись TXT, называющую сам домен.
- Идентичность
- домен
- Доверие
- verified
Путь 2
Домен у вас есть, но отдавать с него файлы нельзя.
Публикуйте на slug на хостинге и направьте одну запись TXT вашего домена на этот хост.
- Идентичность
- домен, подтверждённый через DNS
- Доверие
- verified
Путь 3
Домена нет вообще.
Публикуйте на slug на хостинге. Позже добавьте домен, ничего не потеряв.
- Идентичность
- хост slug
- Доверие
- unverified
Пути 2 и 3 используют хостинг-сервис публикации на <slug>.prodid.org. Войдите на publish.prodid.org по ссылке из письма, создайте сайт, загрузите файл и подключите домен, если он есть. Там же в любой момент можно всё выгрузить или удалить сайт.
Путь 1
Публикация на своём домене
Два файла и одна DNS-запись. Платформа, публикующая от имени магазинов, делает то же самое на домене каждого магазина, поэтому каждый сохраняет свою идентичность и свой уровень доверия.
Отдайте документ обнаружения
Один файл по фиксированному пути, где перечислено, где лежат ваши фиды:
https://acme.example/.well-known/prodid.json.well-known/prodid.jsonapplication/json{ "$type": "org.prodid.feed", "version": 1, "publisher": { "name": "Acme Tekstil", "contact": "data@acme.example" }, "license": "CC0-1.0", "feeds": ["https://acme.example/prodid/items.ndjson"] }Пример из spec/fixtures/v1/examples/prodid.json, собранный из самого файла. $typeОбязательно: да- Всегда org.prodid.feed.
versionОбязательно: да- 1. Документ с 0 принимается обходами, начатыми до 2027-01-26 00:00 UTC, а после отклоняется как однозначный отказ.
publisher.nameОбязательно: да- Имя, которое человек должен видеть рядом с вашими утверждениями.
publisher.contactОбязательно: нет- Адрес почты, который кто-то действительно читает. Туда индекс пишет, когда ваш источник уходит в lapse, и это единственное предупреждение, которое вы получите.
licenseОбязательно: да- Идентификатор SPDX для фактов в ваших фидах, например CC0-1.0.
feedsОбязательно: да- До 100 URL https, все на том же хосте, что и этот документ.
domainОбязательно: при делегировании- Домен, от имени которого говорит документ. Обязателен, когда к документу приходят через запись TXT, называющую другой хост, и тогда должен совпадать с этим доменом. Необязателен, если домен отдаёт документ сам, но если указан, всё равно должен совпадать.
Отдавайте его по HTTPS на стандартном порту, с действующим сертификатом, как
application/jsonили любойapplication/…+json. Не перенаправляйте на другой хост: единственный редирект, по которому идёт обходчик, — сacme.exampleнаwww.acme.example.Отдайте фид
Фид — это NDJSON: один полный JSON-объект на строку, UTF-8, строки разделены переводом строки. Без массива вокруг и без запятых между строками. Так фид на два миллиона товаров читается построчно, а одна плохая строка стоит вам только этой строки, а не остальных.
prodid/items.ndjsonapplication/x-ndjson{"$type":"org.prodid.item","key":"TJ-2041-NVY-M","brand":"Acme","name":"Trail Jacket","lang":"en","group":"TJ-2041","variant":{"color":"navy","size":"M"},"identifiers":[{"scheme":"gtin","value":"09506000134352"},{"scheme":"mpn","value":"TJ-2041-NVY-M"}],"markets":["TR","DE"],"properties":{"material":"100% recycled polyester","madeIn":"TR","weight":{"value":480,"unit":"g"}},"media":[{"url":"https://acme.example/img/tj2041-navy.webp","license":"CC-BY-4.0","kind":"image"}]} {"$type":"org.prodid.item","key":"TJ-2041-NVY-L","brand":"Acme","name":"Trail Jacket","lang":"en","group":"TJ-2041","variant":{"color":"navy","size":"L"},"identifiers":[{"scheme":"gtin","value":"09506000134406"},{"scheme":"mpn","value":"TJ-2041-NVY-L"}],"markets":["TR","DE"],"properties":{"material":"100% recycled polyester","madeIn":"TR","weight":{"value":505,"unit":"g"}},"media":[{"url":"https://acme.example/img/tj2041-navy.webp","license":"CC-BY-4.0","kind":"image"}]} {"$type":"org.prodid.item","key":"TJ-2041-OLV-M","brand":"Acme","name":"Trail Jacket","lang":"en","group":"TJ-2041","variant":{"color":"olive","size":"M"},"identifiers":[{"scheme":"gtin","value":"09506000134413"},{"scheme":"mpn","value":"TJ-2041-OLV-M"}],"markets":["TR"],"properties":{"material":"100% recycled polyester","madeIn":"TR","weight":{"value":480,"unit":"g"}},"media":[{"url":"https://acme.example/img/tj2041-olive.webp","license":"CC-BY-4.0","kind":"image"}]}Три варианта одного товара из spec/fixtures/v1/examples/items.ndjson. У них общий group, поэтому индекс строит из них одно семейство с тремя вариантами. Фиды лежат на том же хосте, что и документ, который их перечисляет. Документ обнаружения, называющий фид на другом хосте, отклоняется целиком, а не частично: вы теряете весь источник, а не один фид, и это считается однозначным отказом, то есть запускает отсчёт lapse, описанный ниже. Отдавать JSON со своего сайта, а NDJSON с отдельного хоста CDN — нормальное желание, но в v1 это не поддерживается. Держите оба на одном хосте.
Первая из этих строк в удобном для чтения виде:
одна запись org.prodid.item{ "$type": "org.prodid.item", "key": "TJ-2041-NVY-M", "brand": "Acme", "name": "Trail Jacket", "lang": "en", "group": "TJ-2041", "variant": { "color": "navy", "size": "M" }, "identifiers": [ { "scheme": "gtin", "value": "09506000134352" }, { "scheme": "mpn", "value": "TJ-2041-NVY-M" } ], "markets": ["TR", "DE"], "properties": { "material": "100% recycled polyester", "madeIn": "TR", "weight": { "value": 480, "unit": "g" } }, "media": [{ "url": "https://acme.example/img/tj2041-navy.webp", "license": "CC-BY-4.0", "kind": "image" }] }$typeОбязательно: да- Всегда org.prodid.item.
keyОбязательно: да- Ваше постоянное имя этой записи внутри фида. Повторная публикация того же key заменяет прежнюю.
brandОбязательно: да- Бренд, как он указан на товаре. Он нормализуется, прежде чем ограничить область mpn, поэтому ARÇELİK и Arçelik — один бренд, а не два.
nameОбязательно: да- Название товара без осей вариантов.
identifiersОбязательно: да- Хотя бы один { scheme, value }, и хотя бы один, который индекс может сохранить. Значения без пробелов по краям и не пустые. gtin должен состоять из 8, 12, 13 или 14 цифр с верной контрольной цифрой и не быть одними нулями; mpn нужен brand, который нормализуется во что-то, потому что уникален он именно в этой области; gtin ограниченного обращения сохраняется, но только как ваш собственный rcn. Запись, в которой не осталось ничего сохраняемого, ничего не идентифицирует и пропускается.
langОбязательно: нет- Тег BCP 47 для человекочитаемого текста в этой записи.
groupОбязательно: нет- Ваш собственный ключ группировки. Items с одинаковым значением — варианты одного семейства.
variantОбязательно: нет- Оси, которыми этот item отличается внутри группы: цвет, размер, объём.
categoryОбязательно: нет- Код брика GS1 GPC в виде gpc: и восьми цифр.
marketsОбязательно: нет- Коды ISO 3166-1 alpha-2 рынков, где продаётся этот item.
propertiesОбязательно: нет- Строки, числа, логические значения, списки или { value, unit } для измерения.
mediaОбязательно: нет- До 20 URL https, у каждого kind и лицензия SPDX из допустимого списка. В отличие от фидов, они могут быть на любом хосте.
Отправляйте плоские items. Вы никогда сами не строите семейства или варианты и никогда не отправляете
opid: первое индекс выводит изgroup, второе присваивает. Если вы уже формируете фид Google Merchant,group— это вашitem_group_id.Перед выгрузкой стоит знать две вещи о штрихкодах.
gtinиз одних нулей — не штрихкод, а заглушка каталога, и запись с ним отклоняется так же, как с неверной контрольной цифрой. Номер ограниченного обращения, то есть внутримагазинные диапазоны, сохраняется и приписывается вам, но глобально ничего не идентифицирует, поэтому никогда не объединяет ваш товар с чужим, и/01/его не разрешает.Закрепите идентичность в DNS
Когда файлы выложены, опубликуйте одну запись, которая называет ваш собственный домен хостом.
_prodid.acme.example TXT_prodid.acme.example. IN TXT "v=prodid0; host=acme.example"Она указывает ровно туда, куда обнаружение и так бы пришло, поэтому с данными ничего не меняется. Меняется то, что может сделать человек, у которого в вашем DNS есть право только добавлять записи. Без записи добавленная им запись тихо забирает вашу идентичность, потому что DNS важнее файла. Если ваша запись уже есть, вторая приводит к ошибке обнаружения больше одной записи — это lapse, видимый, с уведомлением и обратимый.
Никогда не делегируйте
_prodidподрядчику. Право записи в_prodid.<domain>— это контроль над вашей идентичностью, ровно как для DKIM или проверки ACME DNS-01. Домен с общим DNS — это общая идентичность. Отдайте агентству фид, а не запись.Выкладывайте файлы до записи, а не после: запись, указывающая на ещё не существующий документ, — это неудачное обнаружение. Значения читаются без учёта регистра, и одна точка в конце допустима, так что
ACME.Example.иacme.example— один и тот же хост.Сообщите индексу об изменении
Необязательно. Каждый известный домен обходится заново по расписанию, а пинг лишь приближает следующий обход.
попросить индекс запустить обнаружение сейчасcurl -X POST https://prodid.org/ping \ -H 'content-type: application/json' \ -d '{"domain": "acme.example"}' HTTP/1.1 202 Accepted Content-Type: application/json {"status":"accepted"}{"domains": [...]}принимает до 1000 за раз — так платформа представляет все размещённые у неё магазины. Ключ не нужен, потому что пинг не даёт вызывающему никакого влияния на содержимое, а202— всё, что он отвечает. Он не сообщает, был ли домен известен, нашлось ли что-то и не пинговал ли его кто-то минуту назад.429означает лишь, что вы превысили собственный лимит запросов, а ошибки приходят какapplication/problem+json. Что произошло с вашим доменом, видно в статусе источника, а не в пинге.
Путь 2
Делегируйте хостинг, сохраните идентичность
Многие производители владеют доменом, но не могут положить на него файл. Фид размещается в другом месте, а домен подтверждается через DNS, поэтому идентичность источника по-прежнему домен, а уровень доверия по-прежнему verified. Создайте сайт на publish.prodid.org, введите домен — сервис покажет точную запись для добавления и проверит её за вас.
Как это выглядит
Каталог, загруженный на slug на хостинге
, например
acme.prodid.org, который отдаёт и документ обнаружения, и фид.Одна запись TXT на вашем домене
, называющая этот хост. Хост, а не URL, поэтому запись никогда не может направить обходчик на произвольный адрес.
_prodid.acme.example TXT_prodid.acme.example. IN TXT "v=prodid0; host=acme.prodid.org"Документ, который называет ваш домен в ответ.
DNS говорит мой документ вон там; документ говорит я говорю от имени этого домена. Делегированный документ без
domainотклоняется: отсутствие никогда не считается согласием, и именно это не даёт никому направить запись на чужой фид.acme.prodid.org/.well-known/prodid.jsonapplication/json{ "$type": "org.prodid.feed", "version": 1, "domain": "acme.example", "publisher": { "name": "Acme Tekstil", "contact": "data@acme.example" }, "license": "CC0-1.0", "feeds": ["https://acme.prodid.org/feeds/items.ndjson"] }Из spec/fixtures/v1/examples/prodid.delegated.json. Единственное отличие от пути 1 — поле domain, и здесь оно обязательно. Создайте свой сайт
Хостинг-публикатор записывает
domainв документ только после того, как сам увидит вашу запись TXT, поэтому нет момента, когда документ претендует на домен, который об этом не просил.
Путь 3
Публикация без домена
Создайте свой сайт
Производитель без домена публикует на slug на хостинге,
<slug>.prodid.org, под своим именем. Зарезервируйте его на publish.prodid.org. Идентичность источника — хост slug, уровень доверия —unverified, потому что про домен ничего не доказано. Записи — настоящие записи: идентификаторы, разрешение, атрибуция.У большинства таких издателей уже есть файл товаров Google Merchant, поэтому сопоставление такого файла с записями
org.prodid.itemрасписано в спецификации столбец за столбцом, вместе с тем, что намеренно отбрасывается: цена, наличие, доставка и состояние принадлежат продавцу в конкретный день, а не товару. Перед выгрузкой стоит прочитать Импорт фида Merchant: publish.prodid.org преобразует загруженный CSV или TSV ровно по этому сопоставлению. Четыре вещи в нём определяют, какой должна быть ваша таблица:- Четыре столбца становятся осями вариантов:
color,size,materialиpattern— те, между которыми покупатель выбирает внутри одногоitem_group_id.gender,age_group,size_typeиsize_systemостаются обычными свойствами: они описывают всю группу, а как оси разбили бы один товар на несколько, как только кто-то написал бы одно из них двумя способами. - Повторяющийся атрибут задаётся через запятую или повтором заголовка — для файла, а не для ячейки. Ячейка делится по запятым, только если её заголовок встречается в файле один раз; повторите заголовок — и каждая ячейка под ним берётся целиком. Только так можно передать значение с запятой, и это решение принимается один раз на файл. Это касается
additional_image_link,product_type,product_highlight,product_detailиcertification; всё остальное, включаяdescriptionс кучей запятых, в любом случае берётся целиком. product_detailникогда ничего не перезаписывает. Его средний податрибут становится ключом свойства, имя раздела отбрасывается, и побеждает первое значение для ключа. Более поздний detail с тем же ключом или попавший на ключ, уже созданный сопоставлением, отбрасывается и попадает в отчёт со своей строкой, так что столбец detail никогда не может тихо переопределить то, что сказала остальная строка.- Лицензию медиа нужно объявить. В формате Merchant для неё нет столбца, а без лицензии ничего не принимается, поэтому импорт объявляет один допустимый идентификатор SPDX для всех изображений и документов файла. Ваш собственный столбец
licenseпереопределяет его построчно, а файл с недопустимой объявленной лицензией даёт записи без медиа, а не записи с ошибкой.
Строка, которая не может стать записью (нет пригодного идентификатора, у GTIN неверная контрольная цифра), пропускается и попадает в отчёт с номером строки таблицы, где заголовок — строка 1, так что плохая строка стоит только себя, а не всего импорта. Готовый файл для сверки лежит в /spec/fixtures/merchant: CSV, записи, которые он должен дать, и строка, которая должна быть пропущена.
Для решения, принятого сегодня, важно, что будет потом. Запись TXT на домене, который вы приобретёте, с тем же хостом превращает slug в домен при следующем обходе: тот же источник, те же записи, каждый
opidсохранён, а доверие растёт сunverifiedдоverified. Ваши идентификаторыskuпривязаны к самому источнику, а не к его имени, поэтому переживают переезд. На publish.prodid.org это то же поле домена, что и в пути 2.- Четыре столбца становятся осями вариантов:
Проверьте свой домен
Закончили публикацию? Введите свой домен. Индекс получит просьбу проверить его сейчас, а вы перейдёте на его страницу состояния.
Перед запуском
Проверьте файлы перед пингом
prodid-batch check проверяет документ обнаружения и каждый фид ровно так же, как это сделал бы индекс, без токена и не обращаясь к индексу. Он запускается из клона репозитория с Bun 1.4.4. Передайте ему сами файлы: путь, оканчивающийся на .json, читается как один документ, любой другой — как NDJSON-фид.
git clone https://github.com/productdevbook/prodid
cd prodid
bun install --frozen-lockfile
curl -fsSo prodid.json https://acme.example/.well-known/prodid.json
curl -fsSo items.ndjson https://acme.example/prodid/items.ndjson
bun apps/batch/src/cli.ts check prodid.json items.ndjson
prodid.json: ok, 1 record
items.ndjson: ok, 3 recordsФайл без проблем выводит ok и число записей, которые индекс из него возьмёт. Иначе выводится по строке на каждую проблему в виде items.ndjson:12: message с номером строки фида, и программа завершается с ненулевым кодом. Он читает файлы, а не ваш DNS или сервер, так что запись TXT, тип содержимого и заголовки кэширования из списка выше проверять по-прежнему вам.
Правила проверки записи — в спецификации, схемы лежат по адресу /spec/schemas/v1, а /spec/fixtures/v1 содержит машиночитаемые векторы, на которых можно прогнать ваш экспорт до того, как его кто-то обойдёт.
Спецификация публикуется только на английском, и нормативным считается английский текст.
Статус: Spec v1. Индекс и хостинг-публикатор работают на prodid.org. Публиковать может любой: на своём домене или на хостинге. Форматы файлов на этой странице — те, что индекс читает, когда вы его пингуете.
Справочник
Это стоит знать раньше всего остального: от этого зависит, какой из трёх путей ниже ваш. Обнаружение при каждом обходе начинается заново, сначала с DNS, и между обходами ничего о пути к вашим данным не запоминается.
Сначала
DNS-запись TXT на
_prodid.acme.exampleЕсли там есть запись
v=prodid0, обход идёт только по ней, даже если она называет ваш собственный домен. Запись называет хост; документ всегда лежит по адресуhttps://<host>/.well-known/prodid.json.Только если такой записи нет
https://acme.example/.well-known/prodid.jsonДокумент на самом домене. Кто отдаёт домен по HTTPS, тот и говорит от его имени.
DNS идёт первым, потому что это более сильное доказательство: тот, кто может положить файл на хост, не всегда владеет доменом, и владелец должен иметь возможность это перекрыть. Поэтому запись стоит создать, даже если файл вы отдаёте сами; что это даёт, объясняет путь 1, шаг 3.
Ровно одна запись, и за ней ничего. Больше одной записи v=prodid0 с этим именем — ошибка, а не выбор между ними; одна некорректная или слишком длинная — тоже ошибка. Запасного пути нет: если запись есть и не работает, обнаружение на этом заканчивается, а не отправляет обходчик смотреть ваш домен. Переезжаете на другой хост — замените запись, никогда не добавляйте вторую. Грамматика с тестовыми векторами — в спецификации.
Что считается ответом. HTTP 200 не более чем после одного редиректа, тип содержимого JSON, тело, проходящее проверку по схеме фида, и domain, называющий обнаруживаемый домен. Сайт, отвечающий на неизвестные пути HTML-страницей с кодом 200, не отвечает; как и документ, отданный как text/html.
Доверие — свойство источника, а не утверждения. Оно говорит, что доказано о том, кто говорит, и ничего — о том, верен ли конкретный факт. Что с этим делать, решает тот, кто использует данные.
| Уровень | Значение |
|---|---|
| authoritative | Контроль домена доказан, владение брендом подтверждено. |
| verified | Контроль домена доказан — записью TXT или тем, что домен сам отдаёт документ. |
| unverified | Slug на хостинге без домена, импорт набора данных или вклад сообщества. |
Выше unverified может быть только источник, чья идентичность — домен. verified даёт обход. authoritative — нет, потому что никакой обход не докажет, кому принадлежит бренд: его выдаёт оператор индекса, и только по доказательствам.
Доказательство — ваш префикс компании GS1.
Префикс компании GS1 у GTIN, которые ваш источник публикует под брендом, должен быть лицензирован организации, контролирующей ваш домен, как показывает публичная проверка «Verified by GS1». URL этой проверки хранится вместе с выдачей как доказательство.
Выдача называет один бренд, и каждую можно отозвать.
Бренд записывается в нормализованном виде. У источника могут быть выдачи на несколько брендов, и оператор отзывает каждую отдельно.
Выдача не переживает доказательство, на котором стоит.
Ваш источник
authoritativeровно пока доказан контроль над его доменом, он не lapsed и у него есть хотя бы одна неотозванная выдача. Потеряете доказательство домена или уйдёте в lapse — он сразу опустится до того, что поддерживает оставшееся доказательство:verifiedна время lapse, ниже, если и доказательства домена нет, — без чьих-либо действий. Как только все три условия снова выполнены, он сноваauthoritative.Она покрывает источник, а не бренд.
Доверие остаётся свойством источника: выдача на один бренд делает
authoritativeкаждое утверждение вашего источника, в том числе о брендах, на которые выдачи нет.
Чтобы запросить его, сначала публикуйте на своём домене, затем подайте запрос через форму, указав домен, бренд и проверку Verified by GS1 для одного из его GTIN. Оператор проверяет в Verified by GS1 те GTIN, которые ваш источник публикует под этим брендом. Запрос публичный.
Запись с уже существующим key заменяет прежнюю. Отдельного эндпоинта для обновления нет, потому что состояние — это сам фид.
Key, исчезнувший из фида, который обходчик получил целиком, помечает ваши утверждения о нём как отозванные. Сущность и её идентификатор остаются: чужие ссылки на неё работают, как и утверждения других источников.
Когда обход читает документ, где одного из ваших URL фидов больше нет, каждая активная запись этого фида отзывается сразу, как будто исчезли все его keys, и URL больше не запрашивается. Семидневная отсрочка, описанная ниже, тут не действует: убрать URL — значит сказать, что файла больше нет, и индекс верит вам на слово. Если позже снова его перечислить, он вернётся и будет прочитан с нуля. По возможности отдавайте документ с ETag: 304 означает, что список не изменился, и ничего не выводится.
Строка больше 64 KiB, неразбираемая или невалидная, пропускается и учитывается. Поскольку key пропущенной строки неизвестен, фид с пропущенными строками семь дней ничего не отзывает. После этого отсутствующие keys отзываются как обычно. Счётчик публичный, так что фид, тихо теряющий строки, тихим не останется.
Обход успешен, только когда прочитано всё, что перечисляет документ: приемлемый документ и ответ 200 или 304 от каждого фида в нём. Таймаут одного фида не лишает вас остальных (ответившие фиды принимаются, а при неудачном обходе ничего не отзывается), но обход считается транспортной ошибкой, и feeds_failed показывает это в статусе. Поэтому полностью здоровый документ с одним навсегда мёртвым URL фида уходит в lapse по тем же семи дням, что и домен, который вообще перестал отвечать, и через тридцать дней после этого теряет каталог. URL фида, который вы больше не отдаёте, нужно убрать из документа, а не оставлять гнить.
Однозначный отказ — обнаружение, закончившееся без приемлемого документа (нет ни записи, ни документа; запись некорректная, слишком длинная или повторяется; или документ не 200, не JSON, не валиден, больше не совпадает по domain или перечисляет фид вне своего хоста), — сразу переводит источник в lapse. Транспортная ошибка (таймаут DNS, 5xx, 429, достигнутый лимит — на документе или любом фиде) сама по себе ничего не меняет, но семь дней подряд, когда каждая попытка так заканчивается, тоже переводят вас в lapse, так что вечно отдавать ошибки — не способ заморозить свои утверждения.
Эта презумпция принадлежит хосту, а не вам. Она действует только на хосте, где ваш источник уже был подтверждён. На хосте, где он ни разу не подтверждался (новый slug, хост, на который вы только что направили _prodid, всё в путях 2 и 3), любая ошибка, включая таймаут и 5xx, — однозначный отказ и сразу переводит вас в lapse. Это сделано намеренно: иначе подтверждённый источник мог бы направить запись на чужой хост и по тому, начнётся lapse сегодня или через неделю, узнать, работает ли этот хост. Переезд на другой хост из-за этого ничего вам не стоит. Первый успешный обход на новом хосте всё сбрасывает, а lapse остаётся обратимым все 30 дней до какого-либо отзыва.
Источник в lapse не принимает новых утверждений, показывается читателям как lapsed, а его publisher.contact получает уведомление. Обход источника в lapse ничего не записывает, пока не пройдёт полностью; только тогда lapse снимается. После 30 дней непрерывного lapse ваши записи и их активные утверждения отзываются вместе и остаются в истории. Эти 30 дней — ваше окно восстановления, поэтому контактный адрес должен быть таким, который кто-то читает.
Чтобы вернуться, ничего менять не нужно. Отдавайте тот же документ и тот же фид, что и всегда, и всё опубликованное снова утверждается в момент успешного обнаружения, изменился хоть байт или нет: пока у индекса нет от вас ничего активного, он не использует условные запросы, так что неизменённый файл никогда не повод оставаться отозванным.
Каждое утверждение, которое видит читатель, несёт seen_at: не когда факт впервые где-то опубликован, а с какого момента этот источник непрерывно утверждает это значение. Оно переживает каждый повторный обход с тем же значением — в этом и смысл. Отзыв оно не переживает. Оставьте источник в lapse дольше 30 дней, и дата каждого опубликованного вами факта начнётся заново со дня возвращения, потому что весь перерыв за ними не стоял никто проверяемый. Отозванные строки остаются, поэтому ?history=1 показывает перерыв, а не скрывает его; что видит читатель, описано в спецификации.
/source/acme.example — то место, где видно, что что-то не так: результат последнего обхода, каким путём прошло обнаружение, сколько записей изменилось, сколько строк пропущено, сколько фидов не прочитано и в lapse ли вы и с какого времени. Там видно только то, что публикует ваш домен и когда индекс смотрел, но никогда — кто запросил обход.
{
"$type": "org.prodid.source",
"version": 1,
"name": "acme.example",
"kind": "domain",
"trust": "verified",
"verified_by": "dns-txt",
"verified_at": "2026-09-14T09:12:03Z",
"lapsed": false,
"license": "CC0-1.0",
"last_crawl": {
"at": "2026-09-19T06:00:11Z",
"via": "dns-txt",
"document_url": "https://acme.prodid.org/.well-known/prodid.json",
"outcome": "success",
"http_status": 200,
"records_seen": 3,
"records_changed": 1,
"lines_skipped": 0,
"feeds_failed": 0,
"media_failed": 0
}
}Тип определяется чтением байтов; ваш Content-Type — лишь подсказка. Сохраняются шесть типов и ничего больше: JPEG, PNG, GIF, WebP и AVIF как image, PDF как document. Никаких SVG, HTML, архивов и файлов Office — паспорт изделия выкладывается в PDF. Определённый тип и kind записи должны совпадать, иначе запись пропускается.
| Что | Лимит |
|---|---|
| Одно изображение | 20 MiB |
| Один документ | 50 MiB |
| Одна загрузка медиа | 60 s |
| Медиазаписей в одной записи | 20 |
Лицензия проверяется как строка до любой загрузки и должна быть ровно одним из семи идентификаторов SPDX — тех, что разрешают любому распространять файл без изменений, а именно это индекс с ним и делает:
CC0-1.0 CC-BY-4.0 CC-BY-SA-4.0 CC-BY-ND-4.0
CC-BY-3.0 CC-BY-SA-3.0 CC-BY-ND-3.0Всё здесь считается по записям. Отклонённая, слишком большая или недоступная запись стоит только себя: ваша запись никогда не становится невалидной из-за медиа, остальные утверждения остаются, и никакая ошибка медиа не переводит вас в lapse. Она учитывается в media_failed и повторяется при следующем обходе этой записи. Одно работает наоборот по сравнению с фидами: правило одного хоста здесь не действует, так что изображения могут лежать на CDN. Обоснование — в разделах Что принимается и список лицензий.
Ритейлеры, реестры, открытые наборы данных и участники сообщества — такие же источники, как все. Их утверждения приписываются им и никогда не перезаписывают ваши; где вы расходитесь, оба утверждения видны рядом со своими источниками.