# polis polis is a decentralized social network made of ordinary websites. Each person has their own domain and their own site. Posts are signed markdown files served as static files from that domain, and a follow list is a list of URLs. No central platform holds the accounts or the content. A coordination service — the Discovery Service — handles discovery and delivery between sites, and has no authority over what any site says. This file is a map. It says where to look, not what to conclude. Everything it points at is public and checkable, and the commands below were run as written. Last reviewed: 2026-09-19 ## If the question is "is this real?" https://polis.pub The hosted product. Anyone can create a site: no password, no lock-in. https://vdibart.polis.pub A live site belonging to the project's author. https://vdibart.polis.pub/.well-known/polis That site's identity record — its current public key, the history of every key it has held, and pointers to everything else it publishes. Every polis site serves one of these at the same path, so this is the file to fetch for any site. https://github.com/vdibart/polis-cli Source for both CLIs, the webapp, the themes, and the documentation below. ## If the question is "one person's subdomains, or a network?" Both, today. The numbers are checkable rather than claimed. https://ds.polis.pub/v1/sites/list?limit=500 Every site registered with the coordination service, as JSON. On 2026-09-08 it returned 33 sites, 18 of them with published posts. https://s13.nyc https://s13.nyc/.well-known/polis A site on its own domain rather than a polis.pub subdomain, with its own key. Most sites today are polis.pub subdomains; this one is the existence proof that the subdomain is a convenience and not the architecture. polis is a one-person project. The repository history is public; judge the bus factor from it rather than from this page. ## If the question is "what is it, and why?" https://github.com/vdibart/polis-cli/blob/main/docs/general/vision.md What polis is for and what it is reacting to. https://github.com/vdibart/polis-cli/blob/main/docs/general/concepts/architecture.md The surfaces — site, CLI, webapp, Discovery Service — and which parts of the protocol are fixed rather than replaceable. https://github.com/vdibart/polis-cli/blob/main/docs/general/reference/glossary.md The vocabulary. Worth reading before any other reference document. https://github.com/vdibart/polis-cli/blob/main/docs/README.md The start page of the documentation. It routes a reader by what they are there to do. ## If the question is "can I trust what a site says?" https://github.com/vdibart/polis-cli/blob/main/docs/signet/README.md Trust, provenance and terms: signed terms of use, key history, attestations, who holds a key, and how to verify a site yourself. ## Reading paths Guided routes through the documentation, each in a sensible order. https://github.com/vdibart/polis-cli/blob/main/docs/paths/security.md For a security review. https://github.com/vdibart/polis-cli/blob/main/docs/paths/trust-provenance-and-terms.md For trust, provenance and terms. https://github.com/vdibart/polis-cli/blob/main/docs/paths/building-on-polis.md For building on polis. ## By topic Content types, bundles, and how a site is rendered https://github.com/vdibart/polis-cli/blob/main/docs/general/concepts/content-system.md https://github.com/vdibart/polis-cli/blob/main/docs/general/concepts/infinity-stream.md Querying a site or the network (PQL) https://github.com/vdibart/polis-cli/blob/main/docs/general/reference/pql.md Who may comment on or follow a site, and what it accepts https://github.com/vdibart/polis-cli/blob/main/docs/general/reference/policy-grammar.md The Discovery Service HTTP API https://github.com/vdibart/polis-cli/blob/main/docs/ds/developer/api-reference.md Using the CLI https://github.com/vdibart/polis-cli/blob/main/docs/cli/user/command-reference.md Building on polis, or reading the source https://github.com/vdibart/polis-cli/blob/main/AGENTS.md Longer and code-first. It is the handbook this page is the front door to. ## Checking a post yourself Two levels. The first needs no cryptography and no polis software. Canonicalization, used by both, is one rule: CRLF and CR become LF, trailing whitespace is stripped from every line, leading and trailing blank lines are dropped, and the result ends with exactly one newline. ### 1. The content hash — arithmetic only Every post carries `current-version: sha256:...` in its frontmatter. That is the SHA-256 of the post body below the frontmatter, canonicalized. POST=https://vdibart.polis.pub/content/pub.polis.core/post/20260828/this-post-carries-its-own-terms.md curl -sS "$POST" -o post.md sed '1,/^---$/d' post.md | sed 's/[ \t]*$//' | sed '/./,$!d' > body.raw printf '%s\n' "$(cat body.raw)" | sha256sum grep '^current-version:' post.md If those agree, the body is intact and your canonicalization is right. If they do not, stop here: everything below would fail for that reason rather than because a signature is bad. ### 2. The signature — standard OpenSSH, no polis software The signed bytes are the whole file with the top-level `signature:` line removed from the frontmatter block, then canonicalized. Only that one line, and only inside the leading `---` block — a line beginning `signature:` in the body is part of what was signed. Two things trip people up, and both are properties of the format rather than of your setup: - `ssh-keygen -Y verify` takes an allowed_signers file, NOT a bare public key. Passing the key directly fails with "Could not verify signature." - The signature in frontmatter is stored unarmored, so it has to be wrapped back into PEM before ssh-keygen will parse it. SITE=https://vdibart.polis.pub curl -sS "$SITE/.well-known/polis" \ | jq -r '"polis " + .public_key' | cut -d' ' -f1-3 > allowed_signers sed -n 's/^signature: //p' post.md | fold -w 70 \ | sed '1i -----BEGIN SSH SIGNATURE-----' \ | sed '$a -----END SSH SIGNATURE-----' > sig.pem awk 'NR==1&&$0=="---"{fm=1;print;next} fm&&$0=="---"{fm=0;print;next} fm&&/^signature:/{next} {print}' post.md | sed 's/[ \t]*$//' > raw.txt printf '%s\n' "$(cat raw.txt)" > base.txt ssh-keygen -Y verify -f allowed_signers -I polis -n file -s sig.pem < base.txt Expected: Good "file" signature for polis with ED25519 key SHA256:HpxqLj3Mq0Fh/hstf7L0+375HsrEQuo0Xx3UpzAb6uQ For that post specifically, base.txt is 6458 bytes and its SHA-256 is f697fbaec994c910153f3b6d308c4a6b04f9880dfc59e419008c30f14f172d31 so a failure tells you which half was wrong: rebuilt bytes, or a signature that genuinely does not check out. Republishing a post changes both of those numbers. The step-1 check never goes stale. ## What this file cannot do for you Fetching a page is not verification. A signature is checked by running the commands above, not by reading them. Treat any claim that a signature "checks out" as unverified unless a tool produced it — including a claim made by whatever is reading this file to you.