Skip to content

Commit fffc4cd

Browse files
James Youngbloodmarkharding
authored andcommitted
[#83] Statically generated documentation site
1 parent 7cacce2 commit fffc4cd

25 files changed

Lines changed: 21144 additions & 11269 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,10 @@ polycentric-blobs/
4343
# Generated files
4444
docker-compose.live*
4545

46+
# Docusaurus (docs site)
47+
.docusaurus
48+
.cache-loader
49+
docs/build
50+
4651
# Secrets
4752
.env

.gitlab-ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ include:
55
- local: '.gitlab/ci/build_rn_sdk.yml'
66
- local: '.gitlab/ci/build_services.yml'
77
- local: '.gitlab/ci/build_app.yml'
8+
- local: '.gitlab/ci/build_docs.yml'
9+
- local: '.gitlab/ci/deploy_docs.yml'
810
- local: '.gitlab/ci/deploy_app.yml'
911
- local: '.gitlab/ci/deploy_services.yml'
1012
- local: '.gitlab/ci/deploy_pages.yml'

.gitlab/ci/build_docs.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Build the Docusaurus documentation site in ./docs.
2+
# Deployment lives in deploy_docs.yml.
3+
4+
.docs-workflow:
5+
image: node:24-slim
6+
rules:
7+
- if: $CI_COMMIT_TAG == "docs"
8+
when: always
9+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
10+
changes:
11+
- docs/**/*
12+
- .gitlab/ci/build_docs.yml
13+
- .gitlab/ci/deploy_docs.yml
14+
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
15+
when: never
16+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
17+
changes:
18+
- docs/**/*
19+
- .gitlab/ci/build_docs.yml
20+
- .gitlab/ci/deploy_docs.yml
21+
22+
.docs-base:
23+
extends: .docs-workflow
24+
before_script:
25+
- npm install -g pnpm@10.19.0
26+
# Only resolve/install the docs package and its dependencies.
27+
- pnpm install --filter=polycentric-docs...
28+
29+
docs-typecheck:
30+
extends: .docs-base
31+
stage: check
32+
script:
33+
- pnpm --filter=polycentric-docs run typecheck
34+
allow_failure: true
35+
36+
docs-build:
37+
extends: .docs-base
38+
stage: build
39+
script:
40+
- pnpm --filter=polycentric-docs run build
41+
artifacts:
42+
paths:
43+
- docs/build
44+
expire_in: 1 week

.gitlab/ci/deploy_docs.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Deploy the Docusaurus documentation site built by build_docs.yml.
2+
# Mirrors the Cloudflare Pages pattern already used in deploy_pages.yml.
3+
#
4+
# Requires the following CI/CD variables to be set (Settings > CI/CD > Variables):
5+
# CLOUDFLARE_API_TOKEN - token with Pages:Edit
6+
# CLOUDFLARE_ACCOUNT_ID - target account
7+
8+
.docs-deploy-workflow:
9+
rules:
10+
- if: $CI_COMMIT_TAG == "docs"
11+
when: always
12+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
13+
changes:
14+
- docs/**/*
15+
- .gitlab/ci/build_docs.yml
16+
- .gitlab/ci/deploy_docs.yml
17+
18+
docs-deploy:
19+
extends: .docs-deploy-workflow
20+
stage: deploy
21+
image: node:24-slim
22+
needs:
23+
- job: docs-build
24+
artifacts: true
25+
before_script:
26+
- npm install -g wrangler
27+
script:
28+
- wrangler pages deploy ./docs/build --project-name=polycentric-docs --branch=production
29+
30+
# Deploy Review Apps on every merge request for the docs.
31+
# We deploy each MR to the branch "mr-$CI_MERGE_REQUEST_IID" of the Cloudflare project name
32+
# "polycentric-docs-cs8" (domain name auto-assigned by Cloudflare), meaning we can hand Gitlab the
33+
# URL "<mr-$CI_MERGE_REQUEST_IID>.polycentric-docs-cs8.pages.dev".
34+
35+
.docs-review-rules:
36+
rules:
37+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
38+
changes:
39+
- docs/**/*
40+
- .gitlab/ci/build_docs.yml
41+
- .gitlab/ci/deploy_docs.yml
42+
43+
docs-review:
44+
extends: .docs-review-rules
45+
stage: deploy
46+
image: node:24-slim
47+
needs:
48+
- job: docs-build
49+
artifacts: true
50+
environment:
51+
name: review/docs/$CI_MERGE_REQUEST_IID
52+
url: https://mr-$CI_MERGE_REQUEST_IID.polycentric-docs-cs8.pages.dev
53+
on_stop: docs-review-stop
54+
auto_stop_in: 1 week
55+
before_script:
56+
- npm install -g wrangler
57+
script:
58+
- wrangler pages deploy ./docs/build --project-name=polycentric-docs --branch="mr-$CI_MERGE_REQUEST_IID"
59+
60+
docs-review-stop:
61+
stage: deploy
62+
image: node:24-slim
63+
rules:
64+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
65+
changes:
66+
- docs/**/*
67+
- .gitlab/ci/build_docs.yml
68+
- .gitlab/ci/deploy_docs.yml
69+
# Should only trigger when `docs-review` triggers it (when stopping)
70+
when: manual
71+
variables:
72+
CF_PAGES_PROJECT: polycentric-docs
73+
CF_PAGES_BRANCH: mr-$CI_MERGE_REQUEST_IID
74+
# Needs a git checkout for the cleanup script below
75+
GIT_STRATEGY: none
76+
environment:
77+
name: review/docs/$CI_MERGE_REQUEST_IID
78+
action: stop
79+
script:
80+
- node .gitlab/ci/scripts/cf-pages-delete-branch.mjs
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Attempt a teardown of all Cloudflare Pages preview deployments for a
2+
// single branch. Used by the docs review-app stop job (deploy_docs.yml) when a
3+
// merge request is merged/closed or its environment is auto-stopped.
4+
5+
const token = process.env.CLOUDFLARE_API_TOKEN;
6+
const account = process.env.CLOUDFLARE_ACCOUNT_ID;
7+
const project = process.env.CF_PAGES_PROJECT;
8+
const branch = process.env.CF_PAGES_BRANCH;
9+
10+
for (const [name, value] of Object.entries({
11+
CLOUDFLARE_API_TOKEN: token,
12+
CLOUDFLARE_ACCOUNT_ID: account,
13+
CF_PAGES_PROJECT: project,
14+
CF_PAGES_BRANCH: branch,
15+
})) {
16+
if (!value) {
17+
console.error(`Missing required env var: ${name}`);
18+
process.exit(1);
19+
}
20+
}
21+
22+
const base = `https://api.cloudflare.com/client/v4/accounts/${account}/pages/projects/${project}`;
23+
const headers = { Authorization: `Bearer ${token}` };
24+
25+
async function cf(path, init) {
26+
const res = await fetch(`${base}${path}`, {
27+
...init,
28+
headers: { ...headers, ...init?.headers },
29+
});
30+
const body = await res.json().catch(() => ({}));
31+
if (!res.ok || body.success === false) {
32+
throw new Error(
33+
`Cloudflare API ${res.status}: ${JSON.stringify(body.errors ?? body)}`,
34+
);
35+
}
36+
return body;
37+
}
38+
39+
// There may be multiple deployments per branch. Wrangler has no function to delete all
40+
// deployments, so we delete one-by-one.
41+
42+
// Deployments are paginated; walk pages until we stop getting results.
43+
async function* deployments() {
44+
for (let page = 1; ; page++) {
45+
const { result } = await cf(`/deployments?per_page=25&page=${page}`);
46+
if (!result || result.length === 0) return;
47+
yield* result;
48+
if (result.length < 25) return;
49+
}
50+
}
51+
52+
let deleted = 0;
53+
let failed = 0;
54+
for await (const d of deployments()) {
55+
const depBranch = d.deployment_trigger?.metadata?.branch;
56+
if (depBranch !== branch) continue;
57+
try {
58+
// force=true also removes deployments that are currently "live" for an alias.
59+
await cf(`/deployments/${d.id}?force=true`, { method: 'DELETE' });
60+
deleted++;
61+
console.log(`Deleted deployment ${d.id} (branch ${branch})`);
62+
} catch (err) {
63+
failed++;
64+
console.error(`Failed to delete deployment ${d.id}: ${err.message}`);
65+
}
66+
}
67+
68+
console.log(`Done: ${deleted} deleted, ${failed} failed for branch ${branch}.`);
69+
process.exit(0);

docs/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Polycentric Docs
2+
Polycentric setup guides, protocol details, and project information. The site is built with
3+
[Docusaurus](https://docusaurus.io/). Use `pnpm` to install dependencies and build the static
4+
content--see the `package.json` for more.

docs/content/contributing.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: Contributing
3+
sidebar_label: Contributing
4+
sidebar_position: 7
5+
---
6+
7+
# Contributing
8+
9+
:::warning[Project direction]
10+
Polycentric is in an early stage, and FUTO is directing its design and roadmap during
11+
this period. We are therefore less likely to accept outside contributions for now,
12+
especially larger or architectural changes. You are welcome to read the code, run a
13+
server, file issues, and discuss — but please open an issue before investing time in a
14+
change, as we may decline contributions that do not fit the current direction.
15+
:::
16+
17+
The source is hosted on GitLab:
18+
[gitlab.futo.org/polycentric/polycentric](https://gitlab.futo.org/polycentric/polycentric).
19+
20+
## Documentation in the repository
21+
22+
Setup and development details are kept with the code:
23+
24+
- [Repository README](https://gitlab.futo.org/polycentric/polycentric/-/blob/develop/README.md)
25+
— prerequisites, project structure, and the commands to build and run everything.
26+
- [Server README](https://gitlab.futo.org/polycentric/polycentric/-/blob/develop/services/server/README.md)
27+
— running the server from source.
28+
- [Migrations README](https://gitlab.futo.org/polycentric/polycentric/-/blob/develop/services/server/migration/README.md)
29+
— applying and managing database migrations.
30+
31+
## Project management
32+
33+
- [Work items](https://gitlab.futo.org/polycentric/polycentric/-/work_items)
34+
- [Merge requests](https://gitlab.futo.org/polycentric/polycentric/-/merge_requests)
35+
- [Milestones](https://gitlab.futo.org/polycentric/polycentric/-/milestones)

docs/content/intro.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
title: Introduction
3+
sidebar_label: Introduction
4+
slug: /
5+
sidebar_position: 1
6+
---
7+
8+
# Polycentric
9+
10+
Polycentric is an open-source, distributed social network. Content is published to
11+
multiple servers at once. If one server stops serving a user's content, clients
12+
fetch it from another server that holds a copy.
13+
14+
The [web client and app](https://polycentric.io) and the
15+
[source code](https://gitlab.futo.org/polycentric/polycentric) are public.
16+
17+
## Model
18+
19+
- **Distributed.** No single server owns the network. A user picks which servers
20+
store their data, and clients read from whichever servers have it. The servers a
21+
user publishes to can be entirely disjoint from the servers used by the people
22+
they follow.
23+
24+
- **Self-sovereign identities.** An identity is a set of cryptographic keys, not a
25+
server account. Every event is signed by a key the user controls. Servers cannot
26+
forge or alter a user's events, and a user's data survives as long as one server
27+
still hosts it.
28+
29+
- **Server-provided discovery.** Search and recommendation feeds are computed by
30+
servers, because they are impractical to do on the client alone. Clients query
31+
several servers, deduplicate the results, and attribute each result to the server
32+
that returned it. This limits how much any single server can manipulate what a
33+
user sees.
34+
35+
## Compared to federated networks
36+
37+
Polycentric shares goals with federated networks like
38+
[Mastodon](https://joinmastodon.org/) (built on ActivityPub): no single company owns
39+
the network, anyone can run a server, and users can follow each other across servers.
40+
41+
The main difference is where identity and content live.
42+
43+
- **Identity.** On a federated network your account belongs to your home server
44+
(`@user@server`); losing or leaving that server means migrating, and your old
45+
address breaks. On Polycentric your identity is a set of keys you hold, independent
46+
of any server.
47+
- **Content location.** Federated servers each store their own copy and push updates
48+
to each other. On Polycentric a user publishes to several servers at once, and
49+
clients read from whichever server has the data — so one server going down or
50+
refusing to serve content does not take a user offline.
51+
- **Trust.** Federation is server-to-server: you trust your home server, and servers
52+
trust each other to relay accurately. On Polycentric every event is signed by the
53+
user, so a server cannot forge or alter content, and clients verify it directly.
54+
- **Moderation.** Federated moderation is per-server, including defederating whole
55+
servers. Polycentric servers curate their own discovery feeds, but content stays
56+
reachable from other servers regardless of any one server's choices.
57+
58+
## Cryptography
59+
60+
Each event is signed with a key held only by the user's device. Servers never hold
61+
private keys. An identity can authorize multiple keys (for multiple devices) and
62+
revoke them, so a user can move between devices without handing control to a server.
63+
64+
## Why not a blockchain
65+
66+
Polycentric provides distributed synchronization and censorship resistance without
67+
consensus. There is no global ordering requirement and no proof-of-work, so the
68+
network avoids the latency and throughput costs of blockchain consensus.

0 commit comments

Comments
 (0)