Skip to content

About

Project site for Silo — a self-hosted media server on modern infra (Go + Postgres + gRPC).

Topics

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Repository files navigation

Silo Website

The project site for Silo — a self-hosted media server. Built with Astro, deployed to GitHub Pages, AGPL-3.0-or-later.

Local development

bun install
bun run dev      # → http://localhost:4321
bun run build    # → dist/
bun run preview  # serves the built dist/

Node 20+ also works; Bun is preferred for parity with the site's build tooling.

Project layout

src/
├── assets/home/   artwork for the homepage mockups and the rooms around them
├── components/
│   ├── home/      homepage sections, site header, and footer
│   ├── mock/      Silo app screens (tvOS, iPhone) rebuilt for the homepage
│   └── PolicyPage.astro  shell for /privacy and /account-deletion
├── content/docs/  Starlight docs content — Markdown under docs/
├── data/          content + config — edit copy here, not in templates
│   ├── siteConfig.ts   site-wide constants (name, license, repo names)
│   ├── home.ts         homepage copy and mockup content
│   └── download.ts     apps and server install on /download
├── layouts/       Marketing.astro (head, fonts, shell) and MarketingPage.astro
├── pages/         file-based routing — index.astro is the homepage
├── scripts/home.ts  homepage motion: hero focus, library, pinned scroll scene
└── styles/        marketing.css (site design), mock.css (app mockups),
                   docs.css (Starlight theme)

Editing content

To change Edit
Site name, license, repo URLs src/data/siteConfig.ts
Homepage copy, setup steps, FAQ src/data/home.ts
Mockup titles and metadata src/data/home.ts (see its header note)
Download page apps src/data/download.ts
Header and footer links src/components/home/SiteHeader.astro, SiteFooter.astro
Documentation pages src/content/docs/docs/**/*.md
Documentation sidebar src/data/sidebar.mjs
Old documentation URLs src/data/docs-redirects.mjs
Colors, spacing, typography src/styles/marketing.css

Almost every copy change is a data-file edit, not a markup edit. That's deliberate — the components don't need to be touched unless the layout itself changes.

Documentation

Docs are built with Astro Starlight and served under /docs. Add or edit Markdown files in src/content/docs/docs/. New pages are listed in src/data/sidebar.mjs; astro.config.mjs does not need to change.

Organize pages by the reader's task and audience:

  • get-started/: app choice, prerequisites, and the default installation walkthrough.
  • using-silo/: personal settings, client use, and connecting other apps.
  • running-a-server/: administration, integrations, deployment, and operator reference.
  • beta/: features outside the supported 1.0 scope, including audiobooks, ebooks, and Audiobookshelf compatibility. Keep availability tables and feature instructions here, with explicit Beta titles and notices.
  • developers/: API, plugin, and code-contribution entry points.
  • help/: troubleshooting entry points, reports, and documentation contributions.

Each article declares an explicit slug: docs/article-name in its frontmatter. The homepage uses slug: docs. Public URLs are flat and do not include audience, Beta, or release-version directories. Source folders organize contributions; src/data/sidebar.mjs independently organizes navigation by those stable slugs. Changing a title or moving a source file must not silently change its slug.

Use the existing sidebar data file to curate the reading order. Do not add empty pages for planned features. A guide can link to another audience's guide instead of repeating its setup steps. Old published paths are retained in src/data/docs-redirects.mjs; update internal links to canonical paths. Do not add redirects for unpublished pre-1.0 reorganizations. Once 1.0 is published, record URL changes there and preserve useful section anchors. Run bun run test:docs and bun run build when changing this structure.

Documentation release baseline

Each guide's footer shows Last updated from the latest Git commit that changed its source file. Production and preview builds fetch full history so unrelated commits do not change that date. Local uncommitted edits retain the previous commit's date; new uncommitted pages have no date. This is an edit date, not a source-review or feature-acceptance date. Leave lastUpdated out of page frontmatter so Starlight uses Git history.

src/data/docs-release.mjs records the shared release channel and internal source-review baseline. Guides show a short prerelease notice without revision lists or review-method disclaimers. Source revisions and the review date remain in the data file and internal review notes; they do not certify device acceptance.

At 1.0, set the channel to stable, set release to the actual server release, and update the source revisions and review date after reviewing the docs against the release. Record the website commit alongside the server and client versions in the release record. Keep unreleased behavior in PR previews until it ships. There is no /docs/1.0/ namespace or automatic release-publishing workflow.

An article can declare optional requires fields for server, apple, and android. Each value is a quoted release version such as "1.2.0", meaning that version or later. Use these only when a minimum version is established; do not fill them with the milestone target or infer them from an audit date. The site renders the requirements above the guide. These are minimum versions, not introduced-in or last-tested claims. Record platform exceptions in prose.

Beta articles declare beta: true, retain their visible Beta notice, and appear only in the Beta sidebar group. Their flat URLs do not change when they graduate. The build validates slugs, navigation coverage, Beta placement, version fields, the shared baseline, links, and the retained published redirects.

See the manual review for source revisions, executed checks, and procedures still awaiting hands-on validation. The feature map links the 35 milestone features to their guide sources. A mapped page is not a completed acceptance check.

The shared client instructions review records the follow-up source check, combined Apple/Android steps, and server prerequisites for AI features.

The beta audit records the server, Apple, and Android source revisions, coverage limits, and checks for the Beta section. Keep an ordinary guide's reference to a beta feature short and link to that section instead of duplicating instructions. After the 1.0 publication, preserve published URLs with direct redirects when changing slugs.

The extra nested docs/ directory is intentional: Starlight routes pages from src/content/docs/, so nesting the public docs there gives the site the /docs URL prefix while keeping everything in the same Astro project.

Cross-repo release dispatch

When a release is published in silo-server, silo-apple, or silo-android, the site rebuilds automatically. Wire it up by adding this workflow to each app repo:

# .github/workflows/notify-website.yml
name: Notify website
on:
  release:
    types: [published]

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger website rebuild
        env:
          GH_TOKEN: ${{ secrets.WEBSITE_DISPATCH_TOKEN }}
        run: |
          gh api repos/Silo-Server/siloserver.org/dispatches \
            --method POST \
            -f event_type=release-published \
            -f client_payload[repo]=${{ github.repository }} \
            -f client_payload[tag]=${{ github.event.release.tag_name }}

This needs a WEBSITE_DISPATCH_TOKEN secret on each app repo — a fine- grained GitHub PAT scoped to siloserver.org with Contents: Read and Actions: Read & Write permissions. Standard pattern, set once per repo.

The 6-hour cron is a fallback for missed dispatches and edits that happen outside a release (changed README, added a new app, etc).

Pull request checks and previews

Every pull request runs .github/workflows/pr-build.yml: a full bun run build with internal-link validation (starlight-links-validator). The build fails on a broken /docs link or anchor, so fix those before asking for review.

The same workflow builds the site as a preview and hands the output to preview-deploy.yml, which uploads it to Cloudflare Pages and posts one sticky comment on the pull request with the URL:

https://pr-<number>.siloserver-org.pages.dev

The alias is updated after a successful build and deployment. Previews show an orange banner linking back to the pull request, carry noindex, and are deleted by preview-teardown.yml when the pull request is merged or closed (plus a weekly sweep of anything older than 30 days).

Cloudflare can keep serving a deleted deployment on its alias for a while, so teardown first publishes a one-rule redirect to the alias and keeps it. Each page redirects to the same path on siloserver.org, so a page that never shipped gets the site's 404 page. Teardown waits until the alias returns the redirect, then deletes every other deployment for that pull request. If the redirect fails, teardown still deletes the preview. Pull requests that never published a preview get no redirect. To repeat this for an already closed pull request, run Preview teardown manually with its number; without a number the manual run does the weekly sweep. The weekly sweep removes the redirect once it is 30 days old.

The optional preview commit status appears while the build is queued, then reports building, waiting for deployment, deploying, and the final result. Pending or failed statuses link to the workflow; successful ones link to the preview. Build or deployment failures and cancellations report an unavailable preview instead of leaving the status pending. This does not make preview a required merge check; keep only the intended build checks required in rulesets.

preview-progress.yml receives trusted build lifecycle events, including reruns. It and the deployment workflow call preview-report.yml, which serializes status writes per commit using trusted tooling. The reporter checks the current PR head, build run, and attempt, and rejects stale or regressive updates. It requires no Cloudflare credentials and never checks out PR code. These workflow_run changes take effect after they reach the default branch; a PR preview cannot test them live.

scripts/preview-banner.mjs adds the banner to every built HTML file, including standalone pages copied from public/, documentation, and redirects. It runs only when valid preview metadata is supplied. The banner reserves space above navigation and adjusts when its text wraps; production HTML is unchanged.

Reviewing docs changes in a preview

A preview marks what the pull request changed in the docs. A box in the bottom-right corner of every docs page has a Show changes switch, buttons that step through each change across all changed pages, and a list of those pages. Added blocks are outlined in green, changed blocks in amber with the inserted and removed words marked, and removed blocks become a red note that expands to show the old text. Side by side shows the whole list, table, or paragraph from before and after the change, which is easier to read when a section was rewritten. Press ] and [ to step through changes and c to switch the highlights on or off.

When you open a preview again after a new push, Only new since your last visit limits the list to changes you haven't seen. The browser remembers the changes on each preview it has shown, so this works per browser. The preview comment on the pull request lists the changed pages, each linked to its first change.

scripts/preview-changes.mjs does this after a preview build. It builds the commit the pull request is compared with in a temporary worktree: on GitHub, the target branch in the pull request's merge commit; locally, where the branch left origin/main. It then compares the rendered page title and Markdown body of both builds, block by block, and adds the differences and scripts/preview-changes-overlay.js to each docs page. This adds about 15 seconds to a preview build. A failed comparison shows a notice in the box and does not fail the build. The pull request comment comes from .github/preview-tools/docs-changes-summary.cjs, which runs in the trusted deploy workflow and accepts only validated paths, counts, and plain-text titles from the build artifact.

To try it locally, build with preview metadata and serve the result:

PUBLIC_PREVIEW_PR_NUMBER=1 \
PUBLIC_PREVIEW_PR_URL=https://github.com/Silo-Server/siloserver.org/pull/1 \
bun run build && bun run preview

Set PREVIEW_CHANGES_BASE to a commit or branch to compare with something else, or PREVIEW_CHANGES=0 to skip the comparison.

The split into two workflows is deliberate: pr-build.yml runs contributor code, including from forks, with no secrets and no write permissions. preview-deploy.yml holds the Cloudflare token but never checks out or executes pull request code. It checks out deployment tooling from the trusted workflow commit and installs Wrangler with its committed npm lockfile before uploading the built artifact. It verifies that Cloudflare reports terminal deployment success before publishing the preview link. Deploy and teardown share a concurrency queue, so cleanup cannot be overtaken by publication. This serializes preview operations across the project, including weekly sweeps. Keep it that way, and do not add a token to the build job.

For the same reason, the deploy workflow derives the pull request number and commit from the trusted workflow_run event and the GitHub API, never from the artifact. A fork can edit the build workflow and write anything into an artifact, so artifact contents must not decide where a deployment lands or which comment and commit status are written.

Pull requests from forks get previews too. GitHub doesn't link a commit that exists only in a fork to this repository's pull requests, so .github/preview-tools/find-pull-request.cjs also looks the pull request up by the fork's owner and branch from the build run. Either way, the pull request must be open, with the built commit as its head, from the same repository and branch. A first-time contributor's build waits until a maintainer selects Approve and run on the pull request. To stop someone's pull requests from building at all, block them in the organization settings, which stops them from opening pull requests.

One visible consequence: the build job has no GITHUB_TOKEN, so the build-time release lookup in src/data/releases.ts may be rate-limited on shared runners. Client cards then fall back to plain repository links instead of showing a version. That is expected in a preview and never fails the build.

One-time setup

  1. Create a Cloudflare Pages project (direct upload, no Git integration); production stays on GitHub Pages. The project name is set once per workflow as PREVIEW_PROJECT, currently siloserver-org. Keep this value consistent across the three preview workflows.
  2. Create a GitHub environment named Preview, restrict its deployment branches to Selected branches and tags → branch main, and add CLOUDFLARE_API_TOKEN (Account · Cloudflare Pages · Edit, scoped to that one account) and CLOUDFLARE_ACCOUNT_ID as environment secrets. Keeping them out of repository secrets and restricting the environment to main prevents PR workflows from reading them, including workflows edited on same-repository branches. Do not allow refs/pull/*/merge or arbitrary tags.
  3. Require the GitHub Actions build check on main using a branch ruleset or branch protection.

When editing preview workflows, run python3 scripts/test-preview-workflows.py (requires Python 3, Node.js, Bun, and jq). These checks mock the provider APIs to cover cleanup failures, pagination, timestamp formats, deployment status, and PR closure or reopening during queued operations. The PR build runs them before building the site.

Deployment

GitHub Pages, configured by .github/workflows/deploy.yml. The repo is named siloserver.org and the canonical domain is https://siloserver.org, configured via public/CNAME.

If you ever change hosting or domain, override the build with repo variables (vars.SITE, vars.BASE_PATH) — the workflow honors both, so the same code deploys to a different URL shape without code changes.

Cloudflare caching

GitHub Pages remains the origin. Cloudflare caches the public website at the edge, while browsers revalidate pages and files whose URLs can change contents. Files with hashes in their names under /_astro/ can be cached for a year.

Every successful production deployment writes /_build.json with the commit, workflow run, and attempt. The deploy job waits until that marker matches the new artifact, purges the siloserver.org hostname, and checks the homepage. This covers pushes, scheduled builds, release dispatches, and manual deployments. The marker bypasses Cloudflare caching; a failed purge fails the deployment job.

The repository needs CLOUDFLARE_ZONE_ID as an Actions variable and CLOUDFLARE_CACHE_PURGE_TOKEN as an Actions secret. The token needs only Cache Purge permission for this zone. It is sent only to Cloudflare's API.

Design system

The site is dark only, set in Plus Jakarta Sans (loaded from Google Fonts at runtime), with Silo's blue, red, and orange as accent light. The homepage shows the product itself: its mockups rebuild real Silo screens from the tvOS and iOS app source at real device ratios, so check a label or layout against the files listed at the top of src/data/home.ts before changing it. The hero and the pinned TV-and-phone scene sit in painted rooms; every other page uses the docs' dark theme.

Colors live as CSS custom properties at the top of src/styles/marketing.css. The downloadable logo originals live under public/brand/. The Starlight docs are themed via src/styles/docs.css.

License & Trademarks

The site's source is licensed AGPL-3.0-or-later, matching the rest of the Silo project. See LICENSE.

The Silo name, logo, and wordmark are trademarks of Silo Media L.L.C. and are not covered by the AGPL. You're free to fork and redistribute the code, but forks and redistributions must not use the Silo brand as their identity and must remove or replace the brand assets. See TRADEMARK.md for what's permitted — including referential use like "compatible with Silo."

Contributing

Read CONTRIBUTING.md before opening a pull request. Site-wide design, navigation, deployment, and product-claim changes should start as an issue.

About

Project site for Silo — a self-hosted media server on modern infra (Go + Postgres + gRPC).

Topics

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages