FISLFinding Internet Structures & Links

FISLFinding Internet Structures & Links

A CID proves what the bytes are. It does not say where to get them. FISL is an HTTP lookup that answers that question, and atfisl publishes the answers on the AT Protocol.


A CID says what, not where

Content addressing decouples two questions that usually travel together: what the data is, and where you get it. Given a CID, a client can verify the bytes it receives no matter who served them. That leaves one question open, and it is the awkward one — who has them?

RASL answers it with hints baked into a URL at the time of writing. Hints go stale: domains lapse, mirrors move, certificates expire. FISL makes the answer dynamic. A client asks at the moment of retrieval, and gets back what publishers announce right now.


Two specifications

They fit together, and either works without the other.

FISL does not require atfisl. It specifies the query interface and nothing else — how an index collects, validates, and expires records is out of scope. atfisl is one way to fill it.

How it works

Publish

A publisher writes a record to their repository saying “this CID is retrievable at these URLs, until this time”. The record key is the CID itself, so a put replaces the announcement and a delete retracts it. There is no separate revocation mechanism.

Index

An indexer follows the firehose or backfills repositories, verifies each record was in a signed commit, and stores one row per publisher and CID. Indexers store what publishers said, not what is true — they never fetch the URLs.

Look up, then verify

A client sends the CID, gets back locations, fetches whichever transport it supports, and hashes the bytes. A wrong answer costs one failed request, not a corrupted file. That is what makes it safe to take location claims from strangers.


The lookup API

Request
GET /routing/locations/{cid}/{format}

The format is named by the path, not negotiated with Accept, which keeps every answer cacheable under exactly one URL. Indexers must support both json and jsonl.

GET /routing/locations/{cid}/json
{
  "locations": [
    {
      "publisher": "did:plc:ewvi7nxzyoun6zhxrhs64oiz",
      "addrs": [
        {
          "url": "https://berjon.com/.well-known/rasl/bafkreifn5yxi7nkftsn46b6x26grda57ict7md2xuvfbsgkiahe2e7vnq4"
        },
        { "url": "https://mirror.example.com/kitten.jpg", "expires": "2026-09-01T00:00:00Z" }
      ],
      "size": 94201,
      "expires": "2026-09-18T00:00:00Z"
    }
  ]
}
A CID with no records is a 200, not a 404. The empty answer is {"locations": []}. A 404 means the indexer does not support that format, and a 400 means the CID did not parse.

Try it

Because an atfisl record key is the CID, a publisher's PDS answers “do you serve this CID?” in one request — no indexer in the middle. The playground does exactly that, live, against the real network.