Continuous integration
Publish Flick views from GitHub Actions using a delegated PKI credential.
Run flkr publish on merges to main to deploy your app’s Flick views. A delegate credential authorizes the job to publish specific view URIs under your app’s trust root until its expiration.
The runner signs publications with the delegate private key and sends its Biscuit. Flick verifies the grant against the public key published for your trust root.
Set up your trust root
Complete Public-key infrastructure first: declare the trust-root URL in Swift, publish its public key through HTTPS or DNS, and save the root private key in 1Password.
1. Create a publishing credential
With flkr installed, run this on your Mac from the app’s Xcode project directory:
flkr delegate --expires-in-days 365By default, flkr reads the root key at op://Private/Flick SDK Root Authority/password and creates a delegate key pair. Its signed Biscuit authorizes that pair to publish the app’s currently declared view URIs.
The grant expires after 365 days by default. Set --expires-in-days to choose another lifetime.
The command writes flick.credentials.json and adds that default filename to .gitignore. The JSON contains the delegate private key, its Biscuit, and the trust-root and expiration metadata needed for publishing.
2. Save the credential in GitHub
In your app’s repository, open Settings → Secrets and variables → Actions → New repository secret. Name it FLICK_PUBLISHING_CREDENTIAL_JSON and paste the entire file as its value.
Or use the GitHub CLI from that repository:
gh secret set FLICK_PUBLISHING_CREDENTIAL_JSON < flick.credentials.json3. Add the publish workflow
The job compiles your Flick sources against the generated app API. Commit that API alongside flicks/src/, its package files, and its lockfile. This example uses npm:
MyApp/
├── .github/workflows/publish-flicks.yml
├── MyApp.xcodeproj
├── MyApp/
│ └── MyApp+Flick.swift
├── generated/
│ ├── manifest.json
│ ├── package.json
│ └── src/
│ ├── index.js
│ └── index.d.ts
└── flicks/
├── package.json
├── package-lock.json
├── tsconfig.json
└── src/
└── card.tsxAfter changing the Swift integration, run flkr locally and commit the refreshed generated/ files. --host ./generated tells CI to use that snapshot, keeping the publish job independent of an Xcode build.
Compiler access
The publish job needs access to a Flick compiler and its S3-compatible runtime store. Contact hi@flick.fyi to arrange access for CI.
Add the supplied values under Settings → Secrets and variables → Actions:
| GitHub setting | Name | Value |
|---|---|---|
| Variable | FLKR_TSGO_URL | Compiler URL reachable from the runner |
| Secret | FLKR_TSGO_PRIVATE_KEY | Compiler request-signing key |
| Variable | RUNTIME_AWS_ENDPOINT_URL_S3 | Shared runtime store’s S3 endpoint |
| Variable | RUNTIME_AWS_REGION | Store region, or auto for a service that uses it |
| Secret | RUNTIME_AWS_ACCESS_KEY_ID | Shared runtime store access key |
| Secret | RUNTIME_AWS_SECRET_ACCESS_KEY | Shared runtime store secret key |
The store credentials need read and write access to crayon-blobs/runtime/. The delegate credential authorizes app publications; these additional credentials authorize compilation and shared-runtime storage.
GitHub Actions
Save this file at the repository root. It publishes on changes to the Flick sources or generated API on main, and supports a manual run from the Actions tab.
name: Publish Flicks
on:
push:
branches: [main]
paths:
- 'flicks/**'
- 'generated/**'
- '.github/workflows/publish-flicks.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: publish-flicks
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: '24'
package-manager-cache: false
- name: Install flkr and view dependencies
run: |
npm install --global @flickfyi/flkr
npm ci --prefix flicks
- name: Publish all declared views
env:
FLICK_PUBLISHING_CREDENTIAL_JSON: ${{ secrets.FLICK_PUBLISHING_CREDENTIAL_JSON }}
FLKR_TSGO_URL: ${{ vars.FLKR_TSGO_URL }}
FLKR_TSGO_PRIVATE_KEY: ${{ secrets.FLKR_TSGO_PRIVATE_KEY }}
RUNTIME_AWS_ENDPOINT_URL_S3: ${{ vars.RUNTIME_AWS_ENDPOINT_URL_S3 }}
RUNTIME_AWS_REGION: ${{ vars.RUNTIME_AWS_REGION }}
RUNTIME_AWS_ACCESS_KEY_ID: ${{ secrets.RUNTIME_AWS_ACCESS_KEY_ID }}
RUNTIME_AWS_SECRET_ACCESS_KEY: ${{ secrets.RUNTIME_AWS_SECRET_ACCESS_KEY }}
run: |
umask 077
printf '%s' "$FLICK_PUBLISHING_CREDENTIAL_JSON" > "$RUNNER_TEMP/flick.credentials.json"
flkr publish --host ./generated --credential "$RUNNER_TEMP/flick.credentials.json"Open Actions → Publish Flicks to inspect a run. A successful run prints Published card for the example above. Your app receives the publication under its declared trust root.
Every declared view must have an implementation in flicks/src/. Views publish one at a time: if a later publication fails, the job fails and earlier successful publications remain deployed.
To publish one view, add its URI from generated/manifest.json:
flkr publish --host ./generated --credential "$RUNNER_TEMP/flick.credentials.json" \
--view 'apple:bundle-id:com.acme.myapp/myApp#views/card'Renew the credential
Renew before the JSON’s expiresAt date, or after adding or renaming views. A credential’s grant contains the view URIs present when it was issued.
From the app’s project directory, create a replacement in a temporary directory and update the same secret:
credential_dir="$(mktemp -d)"
flkr delegate --expires-in-days 365 --output "$credential_dir/flick.credentials.json"
gh secret set FLICK_PUBLISHING_CREDENTIAL_JSON < "$credential_dir/flick.credentials.json"The next run uses the replacement credential. Both grants remain valid until their expiration while the root public key remains authorized.
Troubleshooting
| Error | Fix |
|---|---|
Publishing credential has expired | Generate a new credential and update the GitHub secret |
Credential trust root does not match | Generate the credential for the trust-root URL in this app’s generated API |
Missing view implementations | Add an implementation for every declared view, or select one with --view |
No discovered root authorizes this publication | Check the public-key discovery document, view grant, and credential expiration |
| Compiler connection failure | Set FLKR_TSGO_URL to a service reachable from GitHub; flkr defaults to a local compiler |
Shared runtime publication requires … | Set the named runtime-store variable or secret in the repository |