Sign in

Local development

Use 127.0.0.1 (not localhost) so sign-in redirects work.

fund.at.*

Specification

at.fund is a funding signal layer for the AT Protocol. It publishes metadata — it does not intermediate or handle payments. Records are DID-signed and live in each steward's own repository, not on a platform. What distinguishes it from prior funding standards: social context. When people in your network endorse a project, that signal is cryptographically verifiable and independently auditable.

v0.1.0 (draft) — April 2026

Prior Art and Lineage

at.fund does not exist in isolation. It inherits from a decades-long lineage of web-native funding signals, each building on the last.

YearStandardWhat it introduced
2002rel="payment"The foundational link relation: "here is where you can pay the author."
2019GitHub FUNDING.ymlPlatform-specific structured funding for repositories.
2019npm fundingPackage-level funding metadata. First to connect funding to the dependency graph.
2020podcast:fundingRSS namespace extension. Brought url + label semantics to syndicated media.
2024funding.jsonComprehensive machine-readable standard. Entity metadata, typed channels, tiered plans.
2025at.fundATProto-native. Combines structured metadata with cryptographic identity and social graph.
rel="payment" → "Support this content" (the primitive signal)
GitHub FUNDING.yml → "Support this project, via these platforms"
npm funding → "Support this dependency" (graph-aware)
podcast:funding → "Support this feed" (syndicated, labeled)
funding.json → "Support this entity" (structured, multi-channel)
at.fund → "Support this entity" (signed, social, graph-aware)

What at.fund adds

  • Cryptographic provenance — records are DID-signed, not just DNS-verified
  • Social context — endorsements from your network surface relevance
  • Dependency awareness — transitive dependency scanning, not just direct
  • Protocol-native — records are stored in the user's own ATProto repository, cryptographically signed by their identity
  • Cross-account references — plans can point to channels in any account, enabling shared payment infrastructure

Architecture: Three Layers

at.fund operates as a three-layer system. Each layer is independent and optional; higher layers provide progressive enhancement.

Layer 1: Contribute "Here is my funding page"
Layer 2: Channels + Plans "Here are my payment endpoints and tiers"
Layer 3: Social Graph "Here is who endorses and depends on me"

Any combination is valid: just a contribute link, channels without plans, social graph without funding data, or the full stack.

Layer 1: Contribute

The simplest possible funding signal — a single URL pointing to where contributions can be made. The ATProto equivalent of <link rel="payment">. Singleton per account (key: literal:self).

FieldTypeRequired
urluriYes
labelstring (≤128)No
createdAtdatetimeNo

Layer 2: Channels and Plans

Structured payment metadata. Each channel and plan is its own record, individually addressable by AT URI. This enables fine-grained updates, cross-account references (a plan in one account can point to a channel in another), and natural protocol-level operations.

Channel

FieldTypeRequired
channelTypestring (≤32)Yes
uriuriNo
descriptionstring (≤500)No
createdAtdatetimeNo

Plan

FieldTypeRequired
statusstring (≤16)No
namestring (≤128)Yes
descriptionstring (≤500)No
amountinteger (smallest unit)No
currencystring (≤3)No
frequencystring (≤16)No
channelsat-uri[]No
createdAtdatetimeNo

Amounts are stored in the smallest currency unit (cents for USD, pence for GBP) to avoid floating-point ambiguity.

Layer 3: Social Graph

These records have no equivalent in prior art. They create a social funding graph on top of the ATProto social graph: endorsements("I vouch for this entity's work") and dependencies("my work depends on this entity"). Endorsement counts are verifiable because they live on each endorser's PDS, not the endorsed project's.

Identity Layer

A participation signal — its existence indicates that this account publishes fund.at records. All fields are optional enrichment. Singleton per account (key: literal:self).

FieldTypeRequired
entityTypestring (≤32)No
rolestring (≤32)No
createdAtdatetimeNo

Namespace Organization

fund.at.actor.*
.declaration — "I exist in the at.fund ecosystem"
fund.at.funding.*
.contribute — "Here is my funding page"
.channel — "Here is a payment endpoint"
.plan — "Here is a funding tier"
fund.at.graph.*
.endorse — "I endorse this entity"
.dependency — "I depend on this entity"

Design Principles

Signal, not platform
at.fund publishes metadata. It never intermediates payments, never holds funds, never takes a commission.
Progressive enhancement
Each layer is optional. A steward with just a declaration gets discovered. Add a contribute URL and you get a card with a button. Add channels and plans for richer cards.
Individual records, not monoliths
Each channel and plan is its own record, individually addressable by AT URI. Fine-grained updates, cross-account references, natural protocol operations.
Steward sovereignty
The steward's PDS repository is the source of truth. Records are DID-signed, giving cryptographic proof of authorship that DNS-based systems cannot provide.
Lenient reader, strict writer
at.fund reads funding data leniently — if a field parses, we use it. But the setup flow writes records strictly, producing well-formed data other consumers can rely on.
Social context is the differentiator
"12 people you follow endorse this project" — social context that emerges from protocol-level records.

Lexicon Reference

Six AT Protocol record types organized into three namespaces — fund.at.actor (identity), fund.at.funding (payment), and fund.at.graph(relationships). Any client can read them directly from a user's repo.

fund.at.actor

fund.at.actor.declaration

key: literal:self

Signals participation in the fund.at ecosystem. Create to join, delete to leave. A backfill service can enumerate all participants by scanning for this record. Optional fields describe the entity type and role.

entityType
string
What kind of entity this account represents. Maps to funding.json entity.type.
role
string
The account's role relative to the projects it stewards. Maps to funding.json entity.role.
createdAt
datetime
When this declaration was created or last updated.

fund.at.funding

fund.at.funding.contribute

key: literal:self

Your funding page — GitHub Sponsors, Open Collective, Patreon, or any URL where people can support you. One record per account.

url
uri
The canonical funding page URL (e.g. GitHub Sponsors, Open Collective, ko-fi, or a custom landing page).
label
string
Human-readable description of the funding page (e.g. 'Support via GitHub Sponsors'). Inspired by rel="payment" title attribute. Optional — clients may derive a label from the URL.
createdAt
datetime
When this record was created or last updated.

fund.at.funding.channel

key: any (channel slug)

A payment channel — a specific place where contributions can be received. Each channel is its own record keyed by a slug ID (e.g. 'github-sponsors'). Individually addressable by AT URI, enabling cross-account references.

channelType
string
Channel category. Maps to funding.json channel.type.
uri
uri
The payment URL for this channel (e.g. https://github.com/sponsors/you). Optional for channels without a public URL (e.g. bank transfers). Maps to funding.json channel.address.
description
string
Human-readable description of this channel.
createdAt
datetime
When this record was created or last updated.

fund.at.funding.plan

key: any (plan slug)

A funding plan or tier with a suggested amount. References channels by AT URI, which may be in this account or any other account. This enables teams to share a common payment channel while each maintainer publishes their own plans.

status
string
Whether this plan is currently accepting contributions. Maps to funding.json plan.status.
name
string
Human-readable plan name (e.g. 'Supporter', 'Sustainer').
description
string
What this plan covers or how the funds are used.
amount
integer
Suggested amount in the smallest currency unit (e.g. cents for USD). Use 0 for 'any amount'. Maps to funding.json plan.amount (converted from whole units).
currency
string
ISO 4217 currency code (e.g. 'USD', 'EUR').
frequency
string
How often the plan is billed. Maps to funding.json plan.frequency.
channels
string[]
AT URIs of fund.at.funding.channel records where this plan can be fulfilled. May reference channels in any account. If omitted, all of this account's channels apply.
createdAt
datetime
When this record was created or last updated.

fund.at.graph

fund.at.graph.dependency

key: any (subject)

One record per upstream project you depend on. The subject field is a DID or hostname. Surfaces the full dependency tree so the infrastructure underneath you gets credit too.

subject
string
The dependency identifier: a DID (did:plc:..., did:web:...) or a hostname (example.com). DIDs are preferred for accounts with AT Protocol identity.
label
string
Human-readable name for this dependency (e.g. 'Bluesky', 'AT Protocol').
createdAt
datetime
When this dependency was declared.

fund.at.graph.endorse

key: any (rkey = endorsed subject)

A public endorsement of any entity you use or value. The record key is the endorsed subject (a DID or hostname), so each entity can only be endorsed once per account. Unlike contribute and dependency (published by builders), endorse is published by users — a protocol-native signal of support. Counts are verifiable because endorsements are stored on each endorser's PDS, not the endorsed project's.

subject
string
The endorsed entity: a DID (did:plc:..., did:web:...) or a hostname (example.com). DIDs are preferred for accounts with AT Protocol identity.
createdAt
datetime
When this endorsement was created.

funding.json Compatibility

at.fund aims for round-trip fidelity with funding.json v1.x. A steward's funding.json can be converted to at.fund records and back without information loss.

funding.json fieldat.fund recordNotes
entity.typefund.at.actor.declaration.entityTypeDirect mapping
entity.rolefund.at.actor.declaration.roleDirect mapping
funding.channels[]fund.at.funding.channel recordsOne record per channel; guid → rkey, type → channelType, address → uri
funding.plans[]fund.at.funding.plan recordsOne record per plan; guid → rkey, amount × 100, channels as AT URIs

Intentionally omitted

funding.json fieldWhy omitted
entity.nameAvailable from the ATProto profile
entity.emailPrivacy concern; not appropriate for a public record
entity.phonePrivacy concern
entity.descriptionAvailable from the ATProto profile
entity.webpageUrlDerivable from the DID document
projects[]Out of scope — at.fund is per-account, not per-project

All string fields with constrained vocabularies use ATProto's knownValues pattern rather than closed enums. New values can be added without breaking existing clients.

Acknowledgements

This specification builds on the work of:

  • Eric Meyer and Tantek Çelikrel="payment" (2002), the foundational web funding signal
  • GitHub — FUNDING.yml (2019), platform-aware funding metadata
  • npm — funding field (2019), dependency-graph-aware funding
  • Podcasting 2.0 / Adam Curry and Dave Jonespodcast:funding (2020), syndicated funding signals
  • FLOSS/fund and the funding.json community — funding.json (2024), the most comprehensive machine-readable funding standard
  • The AT Protocol community — the protocol that makes decentralized, cryptographically-signed records possible

This specification is a work in progress. It always reflects the current definitions in github.com/andyschwab/at.fund.

Integration guides and code examples are coming to a dedicated page.