diff --git a/README.md b/README.md
index e058793..109b74f 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,13 @@
-# Chinmina Bridge: Buildkite/Github OIDC token bridge
+# Chinmina Bridge
-[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fjamestelfer%2Fchinmina-bridge.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fjamestelfer%2Fchinmina-bridge?ref=badge_shield)
+**Connect Buildkite to GitHub with secure, short-lived tokens.**
+Chinmina Bridge allows Buildkite agents to securely generate GitHub API tokens
+that can be used to perform Git or other GitHub API actions. It is intended to
+be an alternative to the use of SSH deploy keys or long-lived Personal Access
+Tokens.
-Allows Buildkite agents to get valid GitHub tokens that can be used to perform
-Git or other GitHub API actions. It is intended to be an alternative to the use
-of SSH deploy keys or long-lived Personal Access Tokens.
+![High level Chinmina diagram](docs/chinmina-high-level.png)
The bridge itself is an HTTP endpoint that uses a [GitHub
application][github-app] to create [ephemeral GitHub access
@@ -13,227 +15,29 @@ tokens][github-app-tokens]. Requests are authorized with a [Buildkite
OIDC][buildkite-oidc] token, allowing a token to be created just for the
repository associated with an executing pipeline.
-The token is created with `contents:read` permissions, and only has access to
-the repository associated with the executing pipeline.
-
-Two endpoints are exposed: `/token`, which returns a token and its expiry, and
-`/git-credentials`, which returns the token and repository metadata in the [Git
-Credentials format][git-credential-helper].
+> [!NOTE]
+> Find out more about Chinmina Bridge is available in the [documentation][docs].
+>
+> This has and expanded [introduction][docs-intro], a [getting
+> started][docs-started] guide and a detailed [configuration
+> reference][docs-config]. This has a more detailed description of the
+> implementation, and clear guidance on how to configuration and installation.
[github-app]: https://docs.github.com/en/apps
[github-app-tokens]: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app
[buildkite-oidc]: https://buildkite.com/docs/agent/v3/cli-oidc
[git-credential-helper]: https://git-scm.com/docs/gitcredentials#_custom_helpers
-## Overview
-
-`chinmina-bridge` is used by jobs running on a Buildkite agent to request tokens
-from Github. These can be used to communicate with the GitHub API or (via Git)
-to enable authenticated Git actions.
-
-Git authentication is facilitated by a [Git credential
-helper](https://github.com/jamestelfer/github-app-auth-buildkite-plugin), which
-communicates with the bridge and supplies the result to Git in the appropriate
-format.
-
-The following sequence illustrates a Git authentication flow facilitated by
-`chinmina-bridge`.
-
-```mermaid
-sequenceDiagram
- box Buildkite Agent
- participant Buildkite Job
- participant Git
- participant Credential Helper
- end
- box Self hosted
- participant Chinmina Bridge
- end
- Buildkite Job->>+Git: clone
- Git ->>+ Credential Helper: get credentials
- Credential Helper->>+Buildkite API: Request Buildkite OIDC token
- Buildkite API->>-Credential Helper: bk-oidc
- Credential Helper->>+Chinmina Bridge: Request GH token (auth bk-oidc)
- Chinmina Bridge->>+Buildkite API: Get Pipeline Details
- Buildkite API-->>-Chinmina Bridge: pipeline-repository
- Chinmina Bridge->>+GitHub: Create Token (auth app JWT)
- GitHub-->>-Chinmina Bridge: app-token
- Chinmina Bridge->>-Credential Helper: bk-oidc
- Credential Helper->>-Git: "x-access-token"/app-token
- Git-->>-Buildkite Job: complete
-```
-
-## Why?
-
-There are two options generally used to authenticate Buildkite agents to GitHub:
-
-1. Via a PAT (owned by a GitHub user) that is saved in the agent S3 secrets bucket
-2. Via a deploy key (registered to a single repository) that is likewise saved to
- S3.
-
-As the organization scales however, the overhead of managing them becomes
-unwieldy, and it can be quite difficult for an organisation to successfully
-manage a rotation scheme.
-
-Unless centralized issuance is practiced as well, both of these schemes can
-produce tokens that are tied to a user, leading to unexpected problems when a
-user leaves the organization. There is also the potential for key material to be
-stored or shared incorrectly, leading to increased possibility of accidental
-leakage.
-
-Lastly, all key material is typically stored in an S3 bucket. This is
-straightforward to configure and maintain, but creates a significant issue in
-the event of an account/bucket breach.
-
-Using a GitHub application to authenticate GitHub actions allows:
-
-1. Access keys for repositories are generated on demand and expire after one
- hour.
-1. The generated tokens are only kept by a build agent for the duration of the
- step, and do not require any other persistence.
-1. The private key for the GitHub application is specific to the
- `chinmina-bridge` service. It can (and should) be rotated, an operation that
- is easy to perform.
-1. Supplied tokens are scoped to just the repositories and actions necessary for
- the requesting pipeline.
-1. Additional Buildkite configuration per repository is not required. If the
- application has access, the agent can request a token for it. No need to
- create PATs or generate keypairs, and no need to upload them in multiple
- places. This allows the an organization to have tighter access control on
- pipeline setup without creating additional support overhead.
-1. Tokens can enable a wider set of actions than simple Git operations (e.g. PR
- comments). This is not yet implemented in `chinmina-bridge`, but is a high
- priority for future enhancement.
-
-Also, since `chinmina-bridge` uses Buildkite's OIDC tokens to authorize requests,
-the claims associated with the token can be used to further refine access to a token.
-
-Github has some [good documentation][gh-deploy-keys] about the pros and cons of
-the application token approach. There are two primary downsides documented:
-
-> - Additional setup is needed to create the GitHub App.
-> - Installation access tokens expire after 1 hour, and so need to be
-> re-generated, typically on-demand using code.
-
-`chinmina-bridge` solves the second problem, by making token generation for a
-pipeline at build time trivial.
-
-
-[gh-deploy-keys]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys#github-app-installation-access-tokens
-
-### What's right for your organization?
-
-To understand what's right for your organization, consider:
-
-- how many pipelines do you have? (That is, how many keys are managed?)
-- how easily are tokens rotated?
-- (related) if the secrets bucket is somehow compromised, how difficult would it be for the organization to respond?
-- if tokens are issued to a user, does a person leaving cause an outage in a build pipeline?
-- what processes/restrictions does your organization have around repository access in GitHub and pipeline creation in Buildkite?
-
-## Limitations
-
-- can only grant `contents:read` access
-- will only grant access to the repository associated with a pipeline
-- if the buildkite user has permissions to modify the pipeline repository, they
- may configure a repository that they don't have access to in GitHub (but is
- accessible in the app). This would allow them to potentially extract code via
- use of the pipeline step configuration. **BUT**:
- - it's OK if your organization members have read access to the same set of
- repositories covered by the `chinmina-bridge` GitHub application.
- **OR**
- - it's OK if your organization controls the creation/configuration of
- pipelines: this restricts the opportunity to misconfigure a pipeline.
-
-## Operations
-
-See the [observability documentation](./docs/observability.md) for more details
-on the information provided by the system when running.
-
-## Configuration
-
-Requirements:
-
-1. A Buildkite organization, and a user with sufficient access to create an API
- token that can be used to get the details of any pipeline that is expected to
- be built.
-1. A Github organization, and a user with sufficient permissions to create a
- Github App and install it into the organization.
-1. Ability to deploy a server that can be accessed by the build agents (for example, an ECS service)
-1. Ability to allow Buildkite agents to download and use a custom plugin _or_
- ability to add a plugin to the default settings of the Buildkite agents.
-
-### Buildkite setup
-
-Create an API key with access to the REST API **only** with access to the `read_pipelines` scope.
-
-Save the key securely: it will be provided to the server in a later step. Use a
-"bot" user to create the token if you can.
-
-### Github setup
-
-1. Create an application in your Github organization
- - The application must request `contents:read`
- - Note the application ID
- - Create and save a private key for the application
-2. Install the application into the Github organization
- - choose the repositories the application will have access to. This is the
- limit of the resources that the application can vend tokens for.
-
-### Configure and deploy the bridge server
-
-The server is a Go application expecting to read configuration from environment
-variables, and can be deployed to a server or as a container.
-
-#### Variables
-
-**Server**
-
-- `SERVER_PORT` (optional, default `8080`): the TCP port the server will listen on.
-- `SERVER_SHUTDOWN_TIMEOUT_SECS` (optional, default `25`): the number of seconds
- the server will wait when asked to terminate with `SIGINT`
-
-**Authorization**
-
-- `JWT_BUILDKITE_ORGANIZATION_SLUG` (**required**): the slug of your Buildkite
- organization. This is the identifier of your organization that appears in your
- Buildkite URLs.
-- `JWT_AUDIENCE` (optional, default=`app-token-issuer`): The expected value of the
- `aud` claim in the JWT. Describes the intended audience of the issued JWT
- token, guards against token reuse. Using a non-default value will require configuration of the credentials helper plugin.
-- `JWT_ISSUER_URL` (optional, default `https://agent.buildkite.com`): the
- expected value of the `iss` claim in the agent JWT. Also used to discover the
- JWKS configuration from the `.well-known` address.
-- `JWT_JWKS_STATIC` (optional): a local JWKS JSON file that can be used instead
- of Buildkite. Used to verify the JWT sent by the Buildkite agents to the
- server. This should only be required for server testing, as agents will only
- create a token using the Buildkite key.
-
-**Buildkite API**
-
-- `BUILDKITE_API_TOKEN` (**required**): The API token created for pipeline
- metadata lookups. **Store securely and provide to the container securely.**
-
-**GitHub API connectivity**
-
-- `GITHUB_APP_PRIVATE_KEY` (**required**): The PEM formatted private key of the
- created Github app. **Store securely and provide to the container securely.**
- This is a highly sensitive credential.
-- `GITHUB_APP_ID` (**required**): The application ID of the Github application
- created above.
-- `GITHUB_APP_INSTALLATION_ID` (**required**): The installation ID of the
- created Github application into your organization.
+[docs]: https://chinmina.github.io
+[docs-intro]: https://chinmina.github.io/introduction/
+[docs-started]: https://chinmina.github.io/guides/getting-started/
+[docs-config]: https://chinmina.github.io/reference/configuration/
## Contributing
-Contributions are welcome.
-
-- `direnv` is the tool for setting up the test environment
-- some variant of docker compose makes it easier to run locally
-- Run `make keygen` to create test keys
-- Execute `git` commands in the `.development/keys` directory. This has git
- configuration set up so it uses a local credential helper that will use the
- keys in the `.development/keys` directory.
+This project welcomes contributions! Take a look at the outstanding issues for
+something to dip your toes into, open an issue to get some input, or raise a PR
+if you're confident.
## License
diff --git a/docs/chinmina-high-level.png b/docs/chinmina-high-level.png
new file mode 100644
index 0000000..d62a341
Binary files /dev/null and b/docs/chinmina-high-level.png differ
diff --git a/docs/kms.md b/docs/kms.md
deleted file mode 100644
index d536e04..0000000
--- a/docs/kms.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# Using KMS to sign GitHub JWTs
-
-It is more secure (though more complicated) to provide Chinmina with an AWS KMS key to sign JWTs for GitHub requests.
-
-## Uploading the KMS key
-
-1. [Generate the private key][github-key-generate] for the GitHub application.
-
-2. Check the private key and convert it ready for upload
- - the key spec for your GitHub key _should_ be RSA 2048. To verify that this is
- the case, run `openssl rsa -text -noout -in yourkey.pem` and examine the
- output.
- - convert the GitHub key from PEM to DER format for AWS:
-
- ```shell
- openssl rsa -inform PEM -outform DER -in ./private-key.pem -out private-key.cer
- ```
-
-3. Follow the [AWS instructions][aws-import-key-material] for importing the
- application private key into GitHub. This includes creating an RSA 2048 key
- of type "EXTERNAL", encrypting the key material according to the instructions
- and uploading it.
-
-4. Create an alias for the KMS key to allow for easy [manual key
- rotation][aws-manual-key-rotation].
-
-> [!IMPORTANT]
-> A key alias is essential to allow for key rotation. Unless you're stopped
-> by environmental policy, use the alias. The key will be able to be rotated
-> without any service downtime.
-
-5. Ensure that the key policy has a statement allowing Chinmina to access the key. The specified role should be the role that the Chinmina process has access to at runtime.
-
- ```json
- {
- "Sid": "Allow Chinmina to sign using the key",
- "Effect": "Allow",
- "Principal": {
- "AWS": [
- "arn:aws:iam::226140413739:role/full-task-role-name"
- ]
- },
- "Action": [
- "kms:Sign"
- ],
- "Resource": "*"
- }
- ```
-
-> [!TIP]
-> Chinmina does not assume a role to access the key. It assumes valid
-> credentials are present for the AWS SDK to use.
-
-## Configuring the Chinmina service
-
-1. Set the environment variable `GITHUB_APP_PRIVATE_KEY_ARN` to the ARN of the **alias** that has just been created.
-
-2. Update IAM for your key
- 1. The KMS key resource policy needs to allow the service to use the key
- _for signing only_.
- 2. The IAM policy for the Chinmina process (i.e. the AWS role available to
- Chinmina when it runs) needs to be able to use the _alias_ created for
- the private key. This is done with a condition in the policy element:
-
- ```json
- {
- "Action": "kms:Sign",
- "Effect": "Allow",
- "Resource": "*",
- "Condition": {
- "StringEquals": {
- "kms:RequestAlias": "alias/chinmina-signing",
- },
- },
- }
- ```
-
- Using the `kms:RequestAlias` condition instead of the fully qualified
- key ARN in the `resource` attribute allows for transparent key rotation
- without service interruption.
-
-[github-key-generate]: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps#generating-private-keys
-[aws-import-key-material]: https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys.html
-[aws-manual-key-rotation]: https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html#rotate-keys-manually
diff --git a/docs/releases.md b/docs/releases.md
deleted file mode 100644
index 023606a..0000000
--- a/docs/releases.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# Release process
-
-In short:
-
-1. Releases are triggered by creating a release tag from `main`. This is currently manual.
-2. Release tags conform to semantic versioning
-3. Commits use conventional commit messages to aid in the changelog creation process
-4. A GoReleaser pipeline is used to create the artifacts
-5. All artifacts (binaries and images) are signed by the build process using `cosign`
-
-## When is a release ready?
-
-Releases are created on an as-needed basis. We prefer multiple, smaller releases over releases that have a greater number of changes.
-
-A release is ready when:
-
-- there are committed changes on `main`, and
-- there is confidence in its stability.
-
-Stability is a pre-requisite for merging, so there should not be significant questions about the appropriateness of a `main` release.
-
-## Triggering a release
-
-Releases are triggered via the creation of a semantic-versioned tag, in the format `vX.Y.Z`. Creation of a tag in this format triggers the automated release process.
-
-Only repository administrators may create a tag in this format.
-
-## Release signing
-
-The [Sigstore][sigstore] ecosystem is leveraged for signing executable release outputs. ([Docs][sigstore-docs].)
-
-- [`cosign`][cosign] is used as the signing CLI tool
-- The [`fulcio`][fulcio] public-good instance is used for ephemeral signing certificates
-- The [`rekor`][rekor] [public-good instance][rekor-search] is used for Certificate Transparency record publishing.
-
-The signing process allows some useful attributes of the binaries to be verified:
-
-- the provider of the identity for the build process (i.e. GitHub Actions)
-- the build process that was used to generate them (both scripts and compute)
-- the Git reference of the code that was used to build the binary
-
-Releases are signed with `cosign`, with transparency records published to the [public-good Rekor instance].
-
-[sigstore]: https://www.sigstore.dev/
-[sigstore-docs]: https://docs.sigstore.dev/
-[cosign]: https://github.com/sigstore/cosign?tab=readme-ov-file
-[fulcio]: https://github.com/sigstore/fulcio?tab=readme-ov-file
-[rekor]: https://github.com/sigstore/rekor?tab=readme-ov-file
-[rekor-search]: https://search.sigstore.dev/
-
-## Testing the release process
-
-It is possible to run GoReleaser locally to test some of the release proceses.
-(`goreleaser` must be available.)
-
-```shell
-# from the root of the local working copy
-goreleaser release --clean --verbose --skip "announce,validate"
-```
-
-This will run the binary and image builds, and publish a temporary image to
-[`ttl.sh`](https://ttl.sh/). Temporary images can be used in local testing with
-`docker compose`.
-
-Some processes are skipped when doing this:
-
-- binary signing
-- image signing
-- changelog generation
-- GitHub release creation
-
-Thus release testing verifies a proportion of the GoReleaser configuration, and allows the image/binary builds to be integration tested.
diff --git a/docs/verifying-releases.md b/docs/verifying-releases.md
deleted file mode 100644
index 93960a7..0000000
--- a/docs/verifying-releases.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Verifying releases with `cosign`
-
-Releases [are signed](./releases.md) with `cosign` as part of the release
-process. The build produces additional attestation bundles during this process,
-which can be used to verify both binaries and Docker images.
-
-For binaries, bundles are present in the `tar.gz` archive created by the
-release. For images, bundles are stored in the OCI registry alongside the image
-itself.
-
-## Obtaining `cosign`
-
-Download from the [`sigstore/cosign` project on GitHub][cosign-download], and
-[verify the release][cosign-verify] as you prefer.
-
-[cosign-download]: https://github.com/sigstore/cosign?tab=readme-ov-file#installation
-[cosign-verify]: https://docs.sigstore.dev/cosign/system_config/installation/#verifying-cosign-releases
-
-## Release identity
-
-The certificates issued by the release are issued for the GitHub Actions OIDC
-provider, and the identity is the executed workflow, referenced by the Git tag
-being built.
-
-| Field | Format |
-|-|-|
-| Issuer | `https://token.actions.githubusercontent.com` |
-| Identity | `https://github.com/jamestelfer/chinmina-bridge/.github/workflows/release.yaml@refs/tags/` |
-
-> [!IMPORTANT]
-> **Git tags are not static:** they can be updated to point to a different
-> commit SHA. Examine the recorded claims for the exact commit.
->
-> There are claims recorded for the exact commit of both the workflow that
-> produced the artifact and the commit that the artifact source was built from.
-
-## Verifying an image release
-
-Images are published to Docker Hub in the `chinmina` repository. The images are
-named `chinmina-bridge` and are labelled with their release tag (`vX.Y.Z`).
-
-An image can be verified with the following `cosign` command:
-
-```shell
-TAG=vX.Y.Z \
-cosign verify "chinmina/chinmina-bridge:$TAG" \
- --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
- --certificate-identity "https://github.com/jamestelfer/chinmina-bridge/.github/workflows/release.yaml@refs/tags/$TAG" \
- --output text
-
-# more details are available if you use JSON output:
-TAG=vX.Y.Z \
-cosign verify "chinmina/chinmina-bridge:$TAG" \
- --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
- --certificate-identity "https://github.com/jamestelfer/chinmina-bridge/.github/workflows/release.yaml@refs/tags/$TAG" \
- --output json | jq
-```
-
-The path `.[].optional.Bundle.Payload.logIndex` is the index entry in the public
-transparency log, recording the details of the signing event. The details of the
-event can be found at: https://search.sigstore.dev/.
-
-## Verifying the binary releases
-
-Download and extract the `tar.gz` of the binary you're interested in. The
-artifacts present include both the binary itself (named `chinmina-bridge`) and
-the signing bundle (`chinmina-bridge.cosign.bundle`).
-
-```shell
-# declare the release details for download
-TAG=vX.Y.Z
-ARCH=arm64
-
-# download the binary
-curl -L -o chinmina-bridge_linux_${ARCH}.tar.gz \
- https://github.com/jamestelfer/chinmina-bridge/releases/download/${TAG}/chinmina-bridge_linux_${ARCH}.tar.gz
-
-# extract to the current directory
-tar xvf chinmina-bridge_linux_${ARCH}.tar.gz
-
-# verify
-cosign verify-blob \
- chinmina-bridge \
- --bundle chinmina-bridge.cosign.bundle \
- --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
- --certificate-identity "https://github.com/jamestelfer/chinmina-bridge/.github/workflows/release.yaml@refs/tags/$TAG"
-
-# peek the details
-jq -r '.rekorBundle.Payload.logIndex | "https://search.sigstore.dev/?logIndex=\(.)"' < chinmina-bridge.cosign.bundle
-
-# open the URL that is shown
-```