Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/design-history.md
Original file line number Diff line number Diff line change
Expand Up @@ -2933,3 +2933,35 @@ resolver and the offer-key cache took an injected one, so a cache on a test or r
clock selected keys at one time and checked the directory's signatures at another. Both
now take a clock. Go's fetcher takes it as an optional trailing argument, so its
released call still compiles, as the client verbs' call options were added.

## A publisher proves its domain with the manifest it serves

The contract carries `RequestDomainVerification` and `ConfirmDomainVerification`, an
ACME-style challenge in which the publisher places a token and the Exchange fetches it.
The publisher guide does not walk through them, because the console publishers onboard
through proves the domain another way: from the `fora.json` the domain serves. One fetch
of that document answers three questions at once. It shows the publisher controls the
domain, it names the account that gets paid, and it lists the catalog contributor the
publisher authorizes. The account travels as a `resource_owner_id` key in the Exchange
entry's `ext`, which the v1 contract does not define: the console writes it and its
Exchange reads it. The Exchange reads the document fresh at registration and again at
every catalog push, there through a short-lived cache, so the proof keeps being renewed
rather than being a token checked once and remembered.

The catalog key follows from who runs the import. Pages are imported automatically, by
the operator, from the sitemap the console found at registration, so the operator holds
the contributor key and serves its directory from a host of its own; the publisher
generates and uploads no key. Authorization stays with the publisher: the manifest the
domain serves names the operator's contributor host, and removing it refuses the imports
that follow, once the Exchange's cached copy has expired, with nothing on the Exchange
side able to restore it. What counts is the document the domain serves, not the file the
console hands over: an edge package in front of `/.well-known/fora.json` answers that
address itself, from the same values held in its settings, so there the authorization
lives in those settings. The edge package carries no secret either; it fetches the
Exchange's public keys.

Neither choice removes anything from the contract. The domain-verification RPCs stay in
v1 for deployments that verify that way, and a publisher that pushes its own catalog with
its own key still can, because the push path accepts the domain's own publisher as well
as any contributor its manifest names. What changed is which path the publisher guide
describes: the guide follows the console, not the other way round.
7 changes: 1 addition & 6 deletions scripts/check-doc-conformance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,8 @@ fi
# appear anywhere — code, docs, config, copyright. The literal is built from two
# pieces below so this script does not contain it verbatim, and therefore scans its
# own file too (no self-exclusion blind spot).
#
# One host is allowed: the publisher console runs under the company's own domain,
# and the site must link to it. The host is assembled from the same two pieces so
# the self-scan rule holds. Any other use of the name is still an error.
old_org='postin''dustria'
console_host="console.fora.${old_org}.com"
org_hits=$(git grep -niF "$old_org" -- . 2>/dev/null | grep -vF "$console_host" || true)
org_hits=$(git grep -niF "$old_org" -- . 2>/dev/null || true)
if [ -n "$org_hits" ]; then
echo "::error::stale '${old_org}' org reference (the project is github.com/FORA-Protocol):"
echo "$org_hits"
Expand Down
18 changes: 11 additions & 7 deletions website/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,23 @@ export default defineConfig({
access: 'public',
default: 'https://pub-onboarding.demo.fora-protocol.org/onboarding',
}),
// Where the onboarding preview sends a publisher who wants to register:
// the publisher console root, which signs a visitor in or lets them
// create an account first. Astro validates a value set in the build
// environment but never the default, so the default must itself be an
// https URL.
// Where the onboarding page's register button and the publisher guide
// send a publisher: the console root, which signs a visitor in, or lets
// them create an account, before anything else. The default is the
// console's address on fora-protocol.org, which redirects to wherever
// the console runs, so moving the console changes that redirect rather
// than this site. Astro validates a value set in the build environment
// but never the default, so the default must itself be an https URL.
FORA_PUBLISHER_CONSOLE_URL: envField.string({
context: 'client',
access: 'public',
url: true,
startsWith: 'https://',
default: 'https://console.fora.postindustria.com/',
default: 'https://console.fora-protocol.org/',
}),
// Where a publisher behind an unsupported CDN reaches us.
// Where a publisher reaches us when the preview cannot take them further:
// a CDN the preview does not recognize, or a domain it could not
// generate a discovery document for.
FORA_PUBLISHER_CONTACT_URL: envField.string({
context: 'client',
access: 'public',
Expand Down
4 changes: 3 additions & 1 deletion website/src/content/docs/architecture/deployment-models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Three deployment models cover the full provider spectrum: enterprise providers w

## Provider Onboarding: Domain Verification (v1.0)

Before any deployment model, the provider must prove domain ownership via ACME HTTP-01 style verification. This is handled by `fora-cli`:
Before any deployment model, the provider must prove it controls its domain. With the contract's domain-verification RPCs, that is an ACME HTTP-01 style challenge, handled by `fora-cli`:

1. Provider runs `fora-cli verify --domain techcrunch.com --exchange exchange.ssp-alpha.com`
2. CLI calls `RequestDomainVerification` -- Exchange returns a challenge token
Expand All @@ -17,6 +17,8 @@ Before any deployment model, the provider must prove domain ownership via ACME H

The Exchange also checks `fora.json` to verify it is listed as an authorized exchange for this provider -- providing double protection.

An Exchange can instead take the `fora.json` the domain serves as that proof, as the Exchange behind a publisher console does; [Publisher Onboarding](/getting-started/publisher-onboarding/) describes that path.

### catalog_contributors in fora.json

Providers can authorize third parties (verification vendors, enrichment services) to push resource metadata on their behalf. This is declared in `fora.json`:
Expand Down
6 changes: 3 additions & 3 deletions website/src/content/docs/getting-started/for-providers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ As a provider, you can sign claims about your own resources — Level 1 self-att

1. You compute a SHA-256 hash of the resource content at publish time (or catalog push time)
2. You sign a `ResourceAttestation` with your Ed25519 key, covering the content hash, quantity estimates, and other claims
3. The attestation is pushed to the Exchange via `CatalogService.PushResources` — the SDK's catalog client in Go, TypeScript or Python signs and sends it; run `ValidateResourceEntry` (or its Python/TypeScript twin) on each entry first, which the SDK also ships (see [Publisher Onboarding](/getting-started/publisher-onboarding/#push-your-catalog-with-the-sdk))
3. The attestation is pushed to the Exchange via `CatalogService.PushResources` — the SDK's catalog client in Go, TypeScript or Python signs and sends it; run `ValidateResourceEntry` (or its Python/TypeScript twin) on each entry first, which the SDK also ships (see [Pushing with the Publisher SDK](/components/content-ingestion/sources/#pushing-with-the-publisher-sdk))
4. When an agent fetches your resource, it can recompute the hash and verify the match

<Aside type="note">
Expand Down Expand Up @@ -220,14 +220,14 @@ No. FORA handles any digital resource — articles, credit reports, court filing
Keep it. `robots.txt` blocks training crawlers. FORA handles real-time inference access (the agent needs a resource right now to complete a task). Different traffic, different treatment.

**What if a bot ignores the CDN block?**
The edge function runs on your CDN — bots can't bypass it. No signed URL = no content. For API-backed resources, the Exchange controls access directly.
The edge function runs on your CDN, so a bot cannot route around it, and an AI agent that announces itself gets no content without a signed URL. A scraper that disguises itself as a browser is your CDN bot protection's job: see [Bot Detection](/components/edge-function/bot-detection/#bot-classification-tiers). For API-backed resources, the Exchange controls access directly.

**What if there's no extension profile for my resource type?**
FORA's core protocol works without profiles. You get discovery, pricing, transactions, delivery, attestation, and disputes out of the box. Profiles add domain-specific metadata richness but aren't required.

## Next Steps

1. **Web-facing content providers**: Ask your Exchange operator if they support FORA. If yes, they deploy everything. If no, [self-service deployment](/architecture/deployment-models/) takes 15 minutes.
1. **Web-facing content providers**: Where your Exchange operator runs a publisher console, register your site there; [Publisher Onboarding](/getting-started/publisher-onboarding/) walks you through it. Otherwise, ask your Exchange operator if they support FORA. If yes, they deploy everything. If no, [self-service deployment](/architecture/deployment-models/) takes 15 minutes.
2. **Data and API providers**: Contact a [Exchange operator](/getting-started/for-exchange-operators/) to discuss integration.
3. **All providers**: Review [How Money Flows](/getting-started/how-money-flows/) to understand the billing chain and pricing models.

Expand Down
4 changes: 2 additions & 2 deletions website/src/content/docs/getting-started/live-demo-music.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ Nothing in this demo is specific to Harmonia. Each line of its feed describes th

The publisher, or a catalog contributor it authorizes, pushes the feed to an Exchange. The Exchange turns each term into a signed offer, and the Broker builds its search index from the `music.*` fields in `ext` of the same feed. These fields follow the [fora-music-v1 profile](/protocol/ext-music/), which defines each field and the search filters an agent can send.

The Exchange can be run for you by an operator, or you can run your own. See [Deployment Models](/architecture/deployment-models/) for the options, [Publisher Onboarding](/getting-started/publisher-onboarding/) for the setup, and [JSONL Ingestion](/protocol/jsonl-ingestion/) for the feed format.
The Exchange can be run for you by an operator, or you can run your own. See [Deployment Models](/architecture/deployment-models/) for the options, [Publisher Onboarding](/getting-started/publisher-onboarding/#without-a-publisher-console) for what a publisher sets up to push its own feed, and [JSONL Ingestion](/protocol/jsonl-ingestion/) for the feed format.

## Further reading

Expand All @@ -579,7 +579,7 @@ The Exchange can be run for you by an operator, or you can run your own. See [De
- [Transaction Flow](/protocol/transaction-flow/) — every message of discovery and purchase, with payloads
- [Extension: fora-music-v1](/protocol/ext-music/) — the `music.*` fields and the music search filters
- [Broker Overview](/components/broker/overview/) — how the Broker searches and ranks offers
- [Publisher Onboarding](/getting-started/publisher-onboarding/) — how a catalog gets onto an Exchange
- [CatalogService API Push](/components/content-ingestion/sources/#source-7-catalogservice-api-push) — how a publisher, or a contributor it authorizes, pushes a catalog to an Exchange

## Credits

Expand Down
Loading
Loading