diff --git a/README.md b/README.md index 6179aba..baa75f1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ # openpreflight website The marketing site, published at **https://openpreflight.xyz**. Astro + -Tailwind v4, static output, one page plus a branded 404. +Tailwind v4, static output. Positioning pages plus a branded 404; how-to lives +on [docs.openpreflight.xyz](https://docs.openpreflight.xyz). Reference documentation lives on a separate site, [openpreflight/docs](https://github.com/openpreflight/docs), at @@ -32,9 +33,12 @@ npm run dev ## Structure -- `src/pages/index.astro`: the marketing page +- `src/pages/index.astro`: the marketing homepage +- `src/pages/`: Product, Why, Self-hosted, Security, and the other hub routes - `src/pages/404.astro`: branded not-found -- `src/components/`: Header, CheckRunPanel, RunFlow +- `src/layouts/MarketingPage.astro`: shared header/footer chrome for inner pages +- `src/lib/site.ts`: nav, footer groups, CTAs +- `src/components/`: Header, CheckRunPanel, RunFlow, Rivelle blocks - `src/layouts/Layout.astro`: head, meta, OG tags, header/footer chrome - `src/styles/global.css`: product-green palette, JetBrains Mono, light/dark via `prefers-color-scheme` diff --git a/public/index.md b/public/index.md index f13834c..36922ba 100644 --- a/public/index.md +++ b/public/index.md @@ -38,6 +38,7 @@ See https://openpreflight.xyz/#demo ## Start here +- [Product](https://openpreflight.xyz/product/) - [Quickstart](https://docs.openpreflight.xyz/start/quickstart/) - [Configuration](https://docs.openpreflight.xyz/start/configuration/) - [Register a GitHub App](https://docs.openpreflight.xyz/setup/github-app/) diff --git a/public/llms.txt b/public/llms.txt index ea91bc4..b7304f7 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -11,6 +11,16 @@ License: Apache-2.0 ## Product +- [Product](https://openpreflight.xyz/product/): How a run happens, configuration, non-goals +- [Why](https://openpreflight.xyz/why/): Problem, shape, ceiling +- [Self-hosted](https://openpreflight.xyz/self-hosted/): You run the worker +- [Security](https://openpreflight.xyz/security/): Claims from the security model only +- [Open source](https://openpreflight.xyz/open-source/): Repos, license, contributing +- [Pipeline](https://openpreflight.xyz/pipeline/): `.ci.yml`, not a check registry +- [Integrations](https://openpreflight.xyz/integrations/): GitHub App, Coolify, Docker, process +- [vs GitHub Actions](https://openpreflight.xyz/compare/github-actions/): Complement, do not replace +- [Concepts](https://openpreflight.xyz/concepts/): Check Run, Binding, Job +- [Use cases](https://openpreflight.xyz/use-cases/): Private repos, self-hosted teams, OSS - [Product overview (Markdown)](https://openpreflight.xyz/index.md): Positioning, what it does, what is out of scope for v1 - [Marketing home](https://openpreflight.xyz/): Human-facing landing page diff --git a/scripts/check-links.mjs b/scripts/check-links.mjs index 4783705..bbd009c 100644 --- a/scripts/check-links.mjs +++ b/scripts/check-links.mjs @@ -14,6 +14,20 @@ const dist = join(root, 'dist'); const required = [ 'index.html', '404.html', + 'product/index.html', + 'why/index.html', + 'self-hosted/index.html', + 'security/index.html', + 'open-source/index.html', + 'use-cases/index.html', + 'use-cases/private-repos/index.html', + 'use-cases/self-hosted-teams/index.html', + 'use-cases/open-source/index.html', + 'concepts/index.html', + 'integrations/index.html', + 'integrations/github-app/index.html', + 'pipeline/index.html', + 'compare/github-actions/index.html', 'favicon.svg', 'favicon.ico', 'favicon-32.png', @@ -99,10 +113,12 @@ for (const file of walkHtml(dist)) { if (!href.startsWith('/')) continue; if (href.startsWith('//')) continue; + const pathOnly = href.split('#')[0] || '/'; if ( - href.startsWith('/_') || - href === '/' || - href.endsWith('.svg') || + pathOnly.startsWith('/_') || + pathOnly === '/' || + pathOnly === '' || + pathOnly.endsWith('.svg') || href.endsWith('.png') || href.endsWith('.ico') || href.endsWith('.xml') || @@ -115,11 +131,11 @@ for (const file of walkHtml(dist)) { continue; } - const normalized = href.replace(/\/$/, '') || ''; + const normalized = pathOnly.replace(/\/$/, '') || ''; const candidates = [ - join(dist, href.replace(/^\//, ''), 'index.html'), + join(dist, pathOnly.replace(/^\//, ''), 'index.html'), join(dist, `${normalized.replace(/^\//, '')}.html`), - join(dist, href.replace(/^\//, '')), + join(dist, pathOnly.replace(/^\//, '')), ]; if (!candidates.some((c) => existsSync(c))) { broken.push(`${rel} → ${href}`); diff --git a/src/components/blocks/footer-01.tsx b/src/components/blocks/footer-01.tsx index 99365cf..697fb09 100644 --- a/src/components/blocks/footer-01.tsx +++ b/src/components/blocks/footer-01.tsx @@ -58,7 +58,7 @@ function Footer01({
& { }; const defaultLinks: SiteHeaderLink[] = [ - { label: "Product", href: "#product" }, - { label: "How it runs", href: "#how" }, - { label: "Pipeline", href: "#pipeline" }, - { label: "Demo", href: "#demo" }, + { label: "Product", href: "/product/" }, + { label: "Why", href: "/why/" }, { label: "Docs", href: "https://docs.openpreflight.xyz" }, + { label: "GitHub", href: "https://github.com/openpreflight/openpreflight" }, ]; function BrandMark() { @@ -89,7 +88,7 @@ function SiteHeader01({
{brand} diff --git a/src/components/templates/saas-landing-01.tsx b/src/components/templates/saas-landing-01.tsx index 3ecb8f6..00a7892 100644 --- a/src/components/templates/saas-landing-01.tsx +++ b/src/components/templates/saas-landing-01.tsx @@ -17,12 +17,9 @@ import { SiteHeader01 } from "@/components/blocks/site-header-01"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; +import { CTA, DOCS, REPO, DEMO_REPO, footerGroups, navLinks, tagline } from "@/lib/site"; import demoRuns from "@/data/demo-runs.json"; -const DOCS = "https://docs.openpreflight.xyz"; -const REPO = "https://github.com/openpreflight/openpreflight"; -const DEMO_REPO = "https://github.com/openpreflight/demo"; - function outcomeLabel(conclusion: string) { switch (conclusion) { case "success": @@ -117,15 +114,9 @@ function SaasLanding01({ className, ...props }: React.ComponentProps<"div">) {
@@ -275,8 +266,8 @@ function SaasLanding01({ className, ...props }: React.ComponentProps<"div">) { Actions workflow to write and no pipeline DSL to learn.

@@ -491,13 +482,57 @@ docker compose -f compose.prod.yaml up -d`}
+
+
+

+ Before and after +

+

+ Same private GitHub repo. Different place the Check Run comes from. +

+
+
+

Without

+

+ Actions-only private CI +

+

+ Workflow YAML, hosted or self-hosted actions/runner, + matrices and caches if you need them. Fine when you want that + orchestration layer. +

+
+
+

With

+

+ Worker you host + Check Run +

+

+ One binary, SQLite, a GitHub App you register, and{" "} + .ci.yml. Logs stay + on your disk. Not a unification of GitLab and Jenkins. +

+
+
+
+
+

What it isn't

- If any of these are requirements for you, this is the wrong + This is not another CI provider and not a replacement for GitHub + Actions. Actions orchestrates. openpreflight is a small Check Runs + runner you host. They can coexist. See{" "} + + openpreflight and GitHub Actions + + . If any of these are requirements for you, this is the wrong tool. None of them are in v1:

diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index cc3292a..3135486 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -12,6 +12,7 @@ const siteOrigin = Astro.site ?? new URL('https://openpreflight.xyz'); const canonical = new URL(Astro.url.pathname, siteOrigin); const ogImage = new URL('/og.png', siteOrigin); const llmsTxt = new URL('/llms.txt', siteOrigin); +const isHome = Astro.url.pathname === '/' || Astro.url.pathname === ''; const markdownAlternate = new URL('/index.md', siteOrigin); const DOCS = 'https://docs.openpreflight.xyz'; const REPO = 'https://github.com/openpreflight/openpreflight'; @@ -32,7 +33,9 @@ const ogAlt = - + {isHome ? ( + + ) : null} {title} diff --git a/src/layouts/MarketingPage.astro b/src/layouts/MarketingPage.astro new file mode 100644 index 0000000..fd402a9 --- /dev/null +++ b/src/layouts/MarketingPage.astro @@ -0,0 +1,38 @@ +--- +import Layout from './Layout.astro'; +import { SiteHeader01 } from '../components/blocks/site-header-01'; +import { Footer01 } from '../components/blocks/footer-01'; +import { CTA, footerGroups, navLinks, tagline } from '../lib/site'; + +interface Props { + title: string; + description: string; +} + +const { title, description } = Astro.props; +--- + + +
+ + +
+
+ +
+
+ +
+
diff --git a/src/lib/site.ts b/src/lib/site.ts new file mode 100644 index 0000000..c16d1c3 --- /dev/null +++ b/src/lib/site.ts @@ -0,0 +1,54 @@ +/** Shared marketing chrome. Keep claims v1-true: GitHub Check Runs CI only. */ + +export const DOCS = "https://docs.openpreflight.xyz"; +export const REPO = "https://github.com/openpreflight/openpreflight"; +export const WEBSITE_REPO = "https://github.com/openpreflight/website"; +export const DOCS_REPO = "https://github.com/openpreflight/docs"; +export const DEMO_REPO = "https://github.com/openpreflight/demo"; + +export const CTA = { + quickstart: `${DOCS}/start/quickstart/`, + github: REPO, + demo: "/#demo", +} as const; + +export const tagline = + "A small CI provider for private repos. One Go binary, one SQLite file: register a GitHub App, enable your repos, and get one Check Run per commit."; + +export const navLinks = [ + { label: "Product", href: "/product/" }, + { label: "Why", href: "/why/" }, + { label: "Docs", href: DOCS }, + { label: "GitHub", href: REPO }, +]; + +export const footerGroups = [ + { + title: "Product", + links: [ + { label: "Product", href: "/product/" }, + { label: "Why", href: "/why/" }, + { label: "Pipeline", href: "/pipeline/" }, + { label: "Integrations", href: "/integrations/" }, + { label: "vs GitHub Actions", href: "/compare/github-actions/" }, + ], + }, + { + title: "Trust", + links: [ + { label: "Self-hosted", href: "/self-hosted/" }, + { label: "Security", href: "/security/" }, + { label: "Open source", href: "/open-source/" }, + ], + }, + { + title: "Use", + links: [ + { label: "Use cases", href: "/use-cases/" }, + { label: "Concepts", href: "/concepts/" }, + { label: "Live demo", href: "/#demo" }, + { label: "Quickstart", href: CTA.quickstart }, + { label: "Documentation", href: DOCS }, + ], + }, +]; diff --git a/src/pages/404.astro b/src/pages/404.astro index 4989588..a912a12 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -1,35 +1,17 @@ --- -import Layout from '../layouts/Layout.astro'; - -const DOCS = 'https://docs.openpreflight.xyz'; +import MarketingPage from '../layouts/MarketingPage.astro'; +import { CTA, DOCS, REPO } from '../lib/site'; --- - -
-

404

-

Page not found

-

- That URL is not on this site. The links below cover everything that is. -

- + +

404

+

Page not found

+

That URL is not on this site. The links below cover everything that is.

+ - +
diff --git a/src/pages/compare/github-actions.astro b/src/pages/compare/github-actions.astro new file mode 100644 index 0000000..5242809 --- /dev/null +++ b/src/pages/compare/github-actions.astro @@ -0,0 +1,46 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA, DOCS, REPO } from '../../lib/site'; +--- + + +

Compare

+

Choose the right layer

+

+ GitHub Actions is orchestration: matrices, caches, artifacts, marketplace + actions, hosted or self-hosted actions/runner. openpreflight is + a Check Runs worker you host. Use them together when that matches the setup. + Do not treat this product as a replacement for Actions YAML. +

+ +

What openpreflight is

+
    +
  • One binary, one SQLite file, on your server
  • +
  • A GitHub App you register
  • +
  • install / test / build from .ci.yml
  • +
  • One Check Run per commit, logs on your disk
  • +
+ +

What it is not

+
    +
  • GitHub Actions YAML or actions/runner
  • +
  • Matrices, caches, or artifacts
  • +
  • A hosted runner fleet
  • +
  • Creating GitHub Apps for you
  • +
+

+ Those sit on + Not in v1. + Neighbors (Woodpecker, Drone, Jenkins) are compared in the + docs, not as fake “we replace + Actions” copy. +

+ + +
diff --git a/src/pages/concepts.astro b/src/pages/concepts.astro new file mode 100644 index 0000000..2909377 --- /dev/null +++ b/src/pages/concepts.astro @@ -0,0 +1,72 @@ +--- +import MarketingPage from '../layouts/MarketingPage.astro'; +import { CTA, DOCS } from '../lib/site'; +--- + + +

Concepts

+

Words the binary uses

+

+ A glossary that matches the docs. Each entry is the thing in v1, not a + metaphor for a future platform. +

+ +

Check Run

+

+ GitHub’s status object on a commit. openpreflight creates one per job and + writes conclusion plus a log tail. It is the product’s hero artifact. + ADR 005. +

+ +

Binding

+

+ A row that says this App, this repo, these branches, these optional command + overrides. No enabled binding, no job. + Bindings. +

+ +

Pipeline (.ci.yml)

+

+ Repo file with install, test, build, + optional runtime and timeout. Commands you write, + not a marketplace of named checks. + Pipeline, + docs. +

+ +

GitHub App

+

+ An App you register. Webhooks, installation tokens, Check Runs. Not OAuth, + not Coolify’s GitHub connector. + Setup, + ADR 003. +

+ +

Job

+

+ One queued or running attempt for an (app, repo, sha). Logs + are files under DATA_DIR. + Logs. +

+ +

Executor

+

+ Process (default) or docker run when runtime: is + set or the job is a fork PR. + ADR 004. +

+ +

Shareable log

+

+ Per-binding opt-in so GET /runs/{id} works without a + session. Treat the URL as a secret. +

+ + +
diff --git a/src/pages/integrations/github-app.astro b/src/pages/integrations/github-app.astro new file mode 100644 index 0000000..ab81c89 --- /dev/null +++ b/src/pages/integrations/github-app.astro @@ -0,0 +1,32 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA, DOCS } from '../../lib/site'; +--- + + +

Integrations / GitHub App

+

Install, bind, first Check Run

+

+ openpreflight does not create the App for you. You register it, paste the + PEM and webhook secret into the UI, enable a binding, and push. +

+
    +
  1. Register the App with the permissions and events in the docs.
  2. +
  3. Point the webhook at https://your-host/webhook/{slug}.
  4. +
  5. Install the App on the org or repos you care about.
  6. +
  7. Enable a binding. Push a commit (or open a PR).
  8. +
+

+ The full checklist, permission table, and event list: + Register a GitHub App. + Bindings: + enable repos. +

+ +
diff --git a/src/pages/integrations/index.astro b/src/pages/integrations/index.astro new file mode 100644 index 0000000..b8964c1 --- /dev/null +++ b/src/pages/integrations/index.astro @@ -0,0 +1,40 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA, DOCS } from '../../lib/site'; +--- + + +

Integrations

+

Surfaces that exist in v1

+

+ This is not a grid of CI vendors. GitLab CI, Jenkins, CircleCI, Buildkite, + and Azure DevOps are not adapters here. +

+ + + + +
diff --git a/src/pages/open-source.astro b/src/pages/open-source.astro new file mode 100644 index 0000000..85ad644 --- /dev/null +++ b/src/pages/open-source.astro @@ -0,0 +1,53 @@ +--- +import MarketingPage from '../layouts/MarketingPage.astro'; +import { CTA, DOCS, DOCS_REPO, REPO, WEBSITE_REPO } from '../lib/site'; +--- + + +

Open source

+

Inspectable worker, public repos

+

+ The worker holds GitHub App PEMs. Open source is how you can read what it + does with them. License is Apache-2.0 on the binary; the two sites are MIT. +

+ +

Repositories

+ + +

Contribute

+

+ Issues and pull requests on the code repo. Development loop: + development. + Website and docs have their own CONTRIBUTING files. There is no foundation, + board, or RFC process to join. +

+ +

Releases

+

+ Changelog lives in the code repo. GitHub Releases (when tagged) publish the + image and binaries. Until a tag exists, run from Compose build or a commit + you trust. +

+ +

Security reports

+

+ SECURITY.md + — not the public issue tracker for vulnerabilities. +

+ + +
diff --git a/src/pages/pipeline.astro b/src/pages/pipeline.astro new file mode 100644 index 0000000..cb93c5a --- /dev/null +++ b/src/pages/pipeline.astro @@ -0,0 +1,58 @@ +--- +import MarketingPage from '../layouts/MarketingPage.astro'; +import { CTA, DOCS, REPO } from '../lib/site'; +--- + + +

Pipeline

+

What runs before the Check Run completes

+

+ There is no check registry. Steps are shell commands in + .ci.yml — illustrations like go test or + npm test are commands you author, not first-class products. +

+ +

The file

+
{`runtime: node:24
+install: npm ci
+test: npm test
+build: npm run build
+timeout: 15m`}
+

+ Sample in the code repo: + examples/.ci.yml. + Default filename is .ci.yml, not openpreflight.yaml. +

+ +

Resolution order

+

Highest first:

+
    +
  1. the repo’s pipeline file
  2. +
  3. the binding’s command overrides
  4. +
  5. + Node defaults from package.json (lockfile install, then + test / build only if those scripts exist) +
  6. +
  7. nothing to run → the check is skipped, not failed
  8. +
+

A failing step stops the run; later steps are reported skipped.

+ +

Runtime

+

+ Omit runtime to run in-process. A non-empty image uses + docker run --rm. If the engine is unreachable, the job fails + instead of falling back. Fork jobs always use Docker. +

+

+ The contract, not this page, is source of truth: + pipelines. +

+ + +
diff --git a/src/pages/product/index.astro b/src/pages/product/index.astro new file mode 100644 index 0000000..383a928 --- /dev/null +++ b/src/pages/product/index.astro @@ -0,0 +1,84 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA, DOCS, REPO } from '../../lib/site'; +--- + + +

Product

+

Self-hosted Check Runs for private repos

+

+ One Go binary and one SQLite file. You register a GitHub App, bind repos in + the UI, and every commit gets a Check Run with logs that live on your server. +

+ +

Fit

+

+ You want CI on private GitHub repositories without GitHub Actions runners + and without a pipeline DSL. You already have a machine. The worker is the + product, not a mode. +

+

+ Full platforms, hosted control planes, and Kubernetes-oriented runners + already fill a different slot. This one does not replace them. See + why it is this shape and + the FAQ. +

+ +

What it is not

+

v1 does not include GitHub Actions YAML, actions/runner, matrices, caches, artifacts, or creating GitHub Apps for you. Those stay on Not in v1.

+ +

How a run happens

+

The same loop as the docs, in marketing nouns:

+
{`GitHub ──POST /webhook/{slug}──► api ──enqueue──► queue.Runner
+                                      │                │
+Browser / CLI ──session/Bearer──► api │                ├── GitHub App (Check Run)
+                                      │                ├── workspace (exact SHA)
+                                      └── SQLite       ├── pipeline (.ci.yml)
+                                                       └── executor (process or docker run)`}
+

+ Webhook → queue → checkout of the immutable SHA → install/test/build → one + Check Run. One live run per commit. Details: + architecture + and + ADR 005. +

+ +

Portable in the real sense

+

+ The same .ci.yml and the same worker run on your host. + That is not “any CI vendor.” There are no GitLab, Jenkins, or CircleCI + adapters. +

+ +

Configuration

+

+ Bindings and Apps are rows in SQLite, edited in the web UI. Optional + .ci.yml in the repo supplies commands; binding overrides and + package.json scripts fill gaps. Resolution order lives in + pipelines. Marketing summary: + /pipeline. +

+ +

Results

+

+ GitHub shows the Check Run. The details URL is GET /runs/{id} + on your instance — session by default, or a shareable log if that binding + opted in. +

+ +

Security

+

+ Claims match the docs only: + security model. + Marketing page: /security. +

+ + +
diff --git a/src/pages/security.astro b/src/pages/security.astro new file mode 100644 index 0000000..1071dbf --- /dev/null +++ b/src/pages/security.astro @@ -0,0 +1,79 @@ +--- +import MarketingPage from '../layouts/MarketingPage.astro'; +import { CTA, DOCS, REPO } from '../lib/site'; +--- + + +

Security

+

What the worker is allowed to do

+

+ This page restates the published security model. It does not add claims. + Source: + security model + and + SECURITY.md. +

+ +

Execution

+

+ Steps run as a local process, or as docker run --rm when + runtime: is set. Job containers drop capabilities, set + no-new-privileges, and do not get the engine socket. Image + names are allow-listed. +

+ +

Network

+

+ GitHub POSTs webhooks to your public HTTPS URL. The worker clones with an + installation token via GIT_CONFIG_* Basic auth — never in the + remote URL — then strips the remote before pipeline steps run. +

+ +

Secrets at rest

+

+ App PEM, webhook secret, and Coolify token columns are AES-256-GCM. + GET responses return a redacted marker. The key is CI_SECRET_KEY. + Rotation uses CI_SECRET_KEY_OLD on boot. +

+ +

Job environment

+

+ Job env is built from scratch: no CI_SECRET_KEY, no PEMs, no + webhook secrets, no Coolify tokens, no installation token. +

+ +

Fork pull requests

+

+ Fork PRs are skipped by default. Opt-in requires a reachable Docker engine + and default_runtime. Fork jobs always run in Docker. +

+ +

Sessions and CSRF

+

+ Session cookies are HttpOnly, Secure behind HTTPS. Browser writes need a + CSRF token. Bearer callers skip CSRF. See + ADR 002. +

+ +

Shareable logs

+

+ A binding can opt into unauthenticated GET /runs/{id}. + Job ids are random UUIDs; treat the link as a secret. +

+ +

Reporting

+

+ Report vulnerabilities as described in + SECURITY.md + (private reporting on the GitHub repo, and + security@openpreflight.xyz). +

+ + +
diff --git a/src/pages/self-hosted.astro b/src/pages/self-hosted.astro new file mode 100644 index 0000000..0cd922f --- /dev/null +++ b/src/pages/self-hosted.astro @@ -0,0 +1,57 @@ +--- +import MarketingPage from '../layouts/MarketingPage.astro'; +import { CTA, DOCS, REPO } from '../lib/site'; +--- + + +

Self-hosted

+

You run the worker. GitHub shows the Check Run.

+

+ There is no hosted openpreflight control plane. Compose or the binary on + your server is the whole deployment. +

+ +

The diagram

+
{`Your infra                         GitHub
+─────────                         ──────
+compose / binary
+  UI + API + webhook + runner  ◄── HTTPS webhooks
+  SQLite in DATA_DIR           ──► Check Runs
+  logs on disk                 ──► details_url (your host)`}
+ +

What you operate

+
    +
  • A public HTTPS URL GitHub can reach (reverse proxy in front of port 8080).
  • +
  • CI_SECRET_KEY — the only required env var. Keep it forever.
  • +
  • SQLite and logs under DATA_DIR. That volume must persist.
  • +
  • A GitHub App you register. The worker does not create Apps for you.
  • +
+

+ Walkthrough: + quickstart + and + deployment. +

+ +

Docker and Coolify are optional

+

+ Process executor is the default. runtime: and fork PRs need a + Docker engine (CI_DOCKER_HOST or a mounted socket). Coolify is + optional inventory, a repo picker, and an install-worker API — not required + to run CI, and not a job runner. +

+

+ There is no first-class Kubernetes operator and no air-gap product. If you + put the binary on an isolated network, that is ordinary self-hosting, not a + documented mode. +

+ + +
diff --git a/src/pages/use-cases/index.astro b/src/pages/use-cases/index.astro new file mode 100644 index 0000000..d4ba1fe --- /dev/null +++ b/src/pages/use-cases/index.astro @@ -0,0 +1,36 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA } from '../../lib/site'; +--- + + +

Use cases

+

Who this is for

+

+ Three honest stories. Not a policy engine, and not one YAML across GitLab + and Jenkins. +

+ + + + +
diff --git a/src/pages/use-cases/open-source.astro b/src/pages/use-cases/open-source.astro new file mode 100644 index 0000000..5333d22 --- /dev/null +++ b/src/pages/use-cases/open-source.astro @@ -0,0 +1,33 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA, DEMO_REPO } from '../../lib/site'; +--- + + +

Use cases / Open source

+

Public PRs, real Check Runs

+

+ openpreflight/demo + is a small Node utility with six pull requests. Each is meant to produce a + Check Run on a self-hosted instance — passing, failing test, failing build, + timeout, skipped, container runtime. +

+

+ The log pages are the same /runs/{id} pages you get + behind auth. Shareable logs are on for that binding only. Until the demo + App is bound, the site links the pull requests and leaves run URLs empty + rather than inventing them. +

+

+ This is contributor-facing proof, not a hosted CI service for arbitrary + public repos. You still run your own worker. +

+ +
diff --git a/src/pages/use-cases/private-repos.astro b/src/pages/use-cases/private-repos.astro new file mode 100644 index 0000000..3553a62 --- /dev/null +++ b/src/pages/use-cases/private-repos.astro @@ -0,0 +1,32 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA, DOCS } from '../../lib/site'; +--- + + +

Use cases / Private repos

+

Check Runs on private code, without Actions

+

+ GitHub already stores the repo. You want a status on the commit. You do not + want to put that work on hosted Actions minutes or write workflow YAML for + install/test/build. +

+

+ Register a GitHub App against those private repos, bind them in the UI, and + the worker clones with an installation token that never lands in the remote + URL. The Check Run is the artifact GitHub already knows how to display. +

+

+ Setup: + GitHub App + and + bindings. +

+ +
diff --git a/src/pages/use-cases/self-hosted-teams.astro b/src/pages/use-cases/self-hosted-teams.astro new file mode 100644 index 0000000..3c7d1b7 --- /dev/null +++ b/src/pages/use-cases/self-hosted-teams.astro @@ -0,0 +1,30 @@ +--- +import MarketingPage from '../../layouts/MarketingPage.astro'; +import { CTA, DOCS } from '../../lib/site'; +--- + + +

Use cases / Self-hosted teams

+

Runner and logs stay on your box

+

+ The team already pays for a VPS or a home-lab machine. openpreflight is a + Compose stack on that machine: UI, webhook, runner, SQLite, logs. +

+

+ GitHub only receives Check Run payloads and a details URL that points at + you. Shareable logs are opt-in per binding. Operations (backups, upgrades, + what a restart does to an in-flight job) are in + operations. +

+

+ Coolify can inventory servers and install the worker. It is not required, + and it does not execute jobs. +

+ +
diff --git a/src/pages/why.astro b/src/pages/why.astro new file mode 100644 index 0000000..6821e25 --- /dev/null +++ b/src/pages/why.astro @@ -0,0 +1,61 @@ +--- +import MarketingPage from '../layouts/MarketingPage.astro'; +import { CTA, DOCS, REPO } from '../lib/site'; +--- + + +

Why

+

CI for private repos, small enough to host

+

+ The problem is not “we need another CI platform.” It is: we want a Check + Run on our private code, on our machine, without learning a second + workflow language. +

+ +

The problem

+

+ GitHub already knows how to show a Check Run. Hosted Actions is the default + path, and it brings YAML, hosted minutes, and a lot of surface area. Full + self-hosted platforms exist for teams that need matrices, caches, and + artifacts. Plenty of people want something in between: the commit is gated, + the logs stay here, and the operator is a binary. +

+ +

The shape

+

+ One process is configurator and worker. One SQLite file holds Apps, + bindings, jobs, and encrypted secrets. You register a GitHub App you own. + Pipelines are install/test/build in .ci.yml, not a new DSL. + Runs are gated on the commit the way Zuul does it — trigger on the check + suite, build the immutable SHA, one live run per commit. The ceiling of + that model is in + ADR 005. +

+ +

Contrast

+

+ Hosted runners and full platforms solve orchestration. This product does + not. It reports Check Runs from a worker you host. GitHub Actions can still + orchestrate everything else. They can coexist; this does not replace + workflow YAML. See + openpreflight and GitHub Actions. +

+ +

The ceiling

+

+ Not in v1: Actions YAML, actions/runner, creating Apps for + you, matrices, caches, artifacts. Jobs on another machine use a Docker + engine (CI_DOCKER_HOST), not Coolify as a job runner. If those + are requirements, pick a different tool — the + docs comparison is honest about + that. +

+ + +
diff --git a/src/styles/global.css b/src/styles/global.css index 337f988..09cce4c 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -355,4 +355,141 @@ a { color: inherit; text-decoration: none; } + +.marketing-article h1 { + font-size: 2.25rem; + font-weight: 600; + letter-spacing: -0.04em; + line-height: 1.1; +} + +.marketing-article .lead { + margin-top: 1.25rem; + font-size: 1.125rem; + line-height: 1.6; + color: var(--muted-foreground); +} + +.marketing-article h2 { + margin-top: 2.75rem; + font-size: 1.35rem; + font-weight: 600; + letter-spacing: -0.03em; +} + +.marketing-article h3 { + margin-top: 1.75rem; + font-size: 1.05rem; + font-weight: 600; +} + +.marketing-article p, +.marketing-article ul, +.marketing-article ol { + margin-top: 0.85rem; + line-height: 1.65; + color: var(--muted-foreground); +} + +.marketing-article ul, +.marketing-article ol { + padding-left: 1.2rem; +} + +.marketing-article li + li { + margin-top: 0.4rem; +} + +.marketing-article a { + color: var(--primary); + text-underline-offset: 4px; +} + +.marketing-article a:hover { + text-decoration: underline; +} + +.marketing-article pre, +.marketing-article .diagram { + margin-top: 1.25rem; + overflow-x: auto; + border: 1px solid var(--border); + border-radius: 0.75rem; + padding: 1rem 1.1rem; + font-family: var(--font-mono); + font-size: 0.8rem; + line-height: 1.55; + color: var(--foreground); +} + +.marketing-article .kicker { + font-family: var(--font-mono); + font-size: 0.875rem; + font-weight: 500; + letter-spacing: 0.04em; + color: var(--primary); +} + +.marketing-article .cta-row { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-top: 2.5rem; +} + +.marketing-article .cta-row a { + display: inline-flex; + align-items: center; + border-radius: 0.375rem; + padding: 0.5rem 1rem; + font-size: 0.875rem; + font-weight: 500; + text-decoration: none; +} + +.marketing-article .cta-row a.primary { + background: var(--primary); + color: var(--primary-foreground); +} + +.marketing-article .cta-row a.secondary { + border: 1px solid var(--border); + color: var(--foreground); +} + +.marketing-article .card-grid { + display: grid; + gap: 0.85rem; + margin-top: 1.5rem; +} + +@media (min-width: 640px) { + .marketing-article .card-grid.cols-2, + .marketing-article .card-grid.cols-3 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +.marketing-article .card-grid a, +.marketing-article .card-grid div { + border: 1px solid var(--border); + border-radius: 1rem; + padding: 1.1rem 1.2rem; + color: inherit; + text-decoration: none; +} + +.marketing-article .card-grid a:hover { + border-color: var(--primary); + text-decoration: none; +} + +.marketing-article .card-grid h3 { + margin-top: 0; +} + +.marketing-article .card-grid p { + margin-top: 0.4rem; + font-size: 0.9rem; +} /* rivelle:theme:end */