Important
Read-only mirror — do not push or open PRs here.
The standalone faroshq/provider-quickstart
repository is automatically synced from the kedge monorepo
faroshq/kedge (path providers/quickstart/)
via splitsh-lite. Every sync force-updates
the mirror, so any direct change here is overwritten. File issues and PRs
against faroshq/kedge instead.
This is also the canonical "copy me" template for a standalone provider repo:
it ships its own Dockerfile and Helm chart (deploy/chart/). The image and
chart are built and published from the kedge monorepo CI (every PR builds
them, so breaks are caught before the sync); the mirror itself carries no
build workflows.
A minimal reference provider proving the kedge plugin surface end-to-end. See docs/providers.md for the architecture this example demonstrates.
- A single binary serving both the UI (HTML page, mounted at
/ui/providers/quickstart/in the portal) and the backend HTTP API (mounted at/services/providers/quickstart/). - The
postMessagehandshake (kedge.ready→kedge.context) — the page receives{ user, tenant, theme, basePath }from the portal shell. - That the hub's auth middleware forwards the user's bearer token to the
provider backend (the
/api/helloresponse includes theX-Kedge-Userheader and the token length).
In one terminal, the provider binary:
cd providers/quickstart
go run .
# listening on :8081In another, the kedge hub (embedded kcp is the easiest path):
./bin/kedge-hub \
--embedded-kcp \
--static-auth-tokens=test:user-default \
--listen-addr=:9443Register the provider via its ProviderCatalogEntry:
kubectl --kubeconfig kcp-admin.kubeconfig \
--context kedge-admin \
ws use root:kedge:providers
kubectl apply -f providers/quickstart/manifest.yamlCheck the hub picked it up:
kubectl get providercatalogentry quickstart -o yaml
# status.conditions[Ready].status: "True"Curl the backend through the hub proxy:
curl -sk -H "Authorization: Bearer test" \
https://localhost:9443/services/providers/quickstart/api/hello | jqExpected response:
{
"message": "hello from the quickstart provider",
"provider": "quickstart",
"servedAt": "2026-05-22T...",
"userHeader": "",
"tokenLength": 11
}tokenLength proves the hub forwarded the Authorization header.
Open the UI in a browser:
https://localhost:9443/ui/providers/quickstart/
You should see the demo HTML page. The "Backend API" section fetches
/services/providers/quickstart/api/hello from the browser, proving the
backend proxy works from the page too.
docker build -t kedge-quickstart-provider:dev providers/quickstartUpdate manifest.yaml:
spec.ui.urlandspec.backend.url→ the in-cluster Service DNS, e.g.http://quickstart.providers.svc.cluster.local:8081spec.serviceAccountNamespace→ the Namespace where the Deployment runs
Then apply the manifest plus a Deployment + Service of your own. A Helm
chart for this provider arrives in Phase 4 (see docs/providers.md).
quickstart uses the hub-provisioned model: you apply the
CatalogEntry and the hub catalog controller creates the provider
workspace, mints the runtime kedge-provider-kubeconfig Secret, and
applies the APIExport. quickstart doesn't read kcp itself, so it just
needs the routing — no kubeconfig.
A provider that does talk to kcp can also self-bootstrap with an init container that holds a kcp admin kubeconfig and mints its own runtime kubeconfig — no hub provisioning step. The infrastructure provider demonstrates this end-to-end; see providers/infrastructure and the "Alternative: self-bootstrap via an init container" section of docs/providers.md. When you graduate this quickstart to a real Helm chart, copy that pattern if your provider needs kcp access.
The platform pieces these depend on land in later phases:
- Heartbeat (
POST /api/providers/{name}/heartbeat) — Phase 1C. - Hub-minted
kedge-provider-kubeconfigSecret — Phase 1B. - A
ProviderBindingand APIBinding flow — Phase 3. - A "Providers" page in the portal — Phase 2.
- A first-party Helm chart — Phase 4.
For now this binary just demonstrates that an arbitrary external HTTP
service can be proxied through the hub at a stable, same-origin URL by
declaring a ProviderCatalogEntry. That's the foundation everything else
sits on.