FlickDocumentation

Public-key infrastructure

Flick uses public-key infrastructure and delegated credentials to provide stateless authentication for the SDK.

Your app names a trust-root URL in its root Swift scope. Flick uses the public key published for that URL to verify who can publish updates to the app. Set the URL on your existing root declaration:

import FlickSDK

// Other app declarations…

// Your app’s trust-root URL: Flick discovers its public key via HTTPS or DNS
// to verify permission to publish updates to this app.
@FlickScope(url: "https://acme-corp.com")
enum App {
    // Your scopes, views, state, and actions…
}

When you publish a Flick under this trust root, Flick verifies that the root authority authorized the publication before accepting it. It first looks for the root public key at https://acme-corp.com/.well-known/flick. That endpoint serves a JSON array binding the trust-root URL to your app’s bundle ID and public key:

[
  {
    "iss": "https://acme-corp.com",
    "sub": "com.acme.myapp",
    "alg": "ES256",
    "kid": "<root public key>"
  }
]

Here, iss is the trust-root URL, sub is the bundle ID, and kid is the Base58-encoded root public key. alg identifies the signing algorithm. If the endpoint returns 404 or 410, Flick falls back to DNS TXT records, checking the domain and then its parents as described in Key discovery.

The root key grants publishing permission through a signed Biscuit, using the delegation model described in the Macaroons paper. When you publish with flkr, it reads the root private key from 1Password and creates a one-hour Biscuit authorizing a delegate key to publish that view. It can reuse an existing local identity key; otherwise it generates a temporary key pair.

flkr includes the Biscuit with the publication and signs the request with the delegate private key. Flick uses the discovered root public key to verify the Biscuit, then checks the publisher, view, and expiration before accepting the update.

1. Create the root key pair

A key pair is generated automatically in your browser. Save it below. Enter the trust-root URL and app bundle ID to prepare the public-key document for the next step.

Save as Flick SDK Root Authority in your Private vault. Requires the 1Password browser extension.

2. Publish the public key

The JSON above binds your trust-root URL (iss) and bundle ID (sub) to the public key (kid). Use it unchanged after entering your own URL and bundle ID. The URL must match the Swift declaration exactly, including any path or trailing slash.

You can publish this document via HTTPS or DNS.

2.1. Publish via HTTPS

For HTTPS discovery, serve the JSON at https://acme-corp.com/.well-known/flick with a JSON content type and no redirect. Check the response with:

curl --fail https://acme-corp.com/.well-known/flick

Compare the response with the generated JSON: iss must equal the Swift scope URL, sub must equal your app’s bundle ID, and kid must contain the generated public key.

2.2. Publish via DNS

For DNS discovery, encode the JSON array as UTF-8, then base64url without padding (no trailing = characters). Publish that encoded value as a TXT record at _flick.acme-corp.com. Copy DNS TXT value above performs this encoding for you.

Generate the DNS TXT value with Node.js

This example uses your generated public key, trust-root URL, and bundle ID:

Generating your public key…

Paste the output into your DNS TXT record. Node’s built-in base64url encoding omits padding automatically.

The well-known endpoint must return 404 or 410 for Flick to reach DNS. See Key discovery for the complete lookup order and stopping conditions.

3. Publish a Flick

Install flkr:

npm install -g @flickfyi/flkr

The app’s Swift declarations, generated contract, and Flick implementations live together:

Project layout
MyApp/
├── MyApp.xcodeproj
├── MyApp/
│   └── FlickScopes.swift
├── generated/
│   ├── package.json
│   └── src/
│       ├── index.js
│       ├── index.d.ts
│       └── …
└── flicks/
    ├── package.json
    ├── tsconfig.json
    └── src/
        └── card.tsx

From your app’s Xcode project directory, publish all views:

flkr publish

flkr refreshes generated/ from your app’s Swift declarations using the Swift SDK’s generator. This declares a card view with no input properties:

MyApp/FlickScopes.swift
import FlickSDK

@FlickScope(url: "https://acme-corp.com")
enum App {
    enum Views {
        struct Card: Codable, Sendable {}
    }
}

flkr looks for .ts and .tsx implementations under flicks/src/. flkr creates flicks/package.json and flicks/tsconfig.json with the SDK dependencies, Photon JSX settings, and strict type checking.

The TypeScript file default-exports a defineView binding to the matching generated descriptor:

flicks/src/card.tsx
import { defineView, Text } from '@flickfyi/core'
import { App } from 'com.acme.myapp'

export default defineView(App.views.card, function Card() {
  return <Text>Hello from Flick</Text>
})

Here, com.acme.myapp is the package name in generated/package.json, and App.views.card comes from the Swift App.Views.Card declaration. flkr publish publishes every declared view’s implementation. Each declared view must have an implementation before publishing.

Set up CI credentials

To publish from CI, create a delegate credential for the job. Run this from the app’s Xcode project directory:

flkr delegate --output flick.credentials.json

This reads the root key once and writes a new delegate key pair and its Biscuit to the JSON file. The file also records the trust root, root public key, algorithm, expiration, and format version. The grant covers the app’s declared views and expires after 365 days. Use --expires-in-days 30 to choose a different lifetime.

Store the file as a CI secret. It contains a private key; keep it out of source control. Make the app’s generated/ contract and flicks/ sources available to the job, install the flicks/ dependencies, then pass the credential path when publishing:

flkr publish --host ./generated --credential ./flick.credentials.json --view 'apple:bundle-id:com.acme.myapp/app#views/card'

--host uses the existing generated contract. Use a view URI from that contract. flkr signs with the file’s private key and includes its Biscuit. A supplied credential takes precedence over the root-key configuration. Create a replacement credential before its delegation expires.

Key discovery

For https://preview.acme-corp.com/team, Flick checks these locations in order:

OrderLocationWhen Flick continues
1https://preview.acme-corp.com/.well-known/flickOnly after HTTP 404 or 410
2TXT _flick.preview.acme-corp.comOnly when no records exist
3TXT _flick.acme-corp.comEnd of search

The HTTPS lookup always uses the origin’s /.well-known/flick, even when the issuer has a path. Every binding must retain the full issuer—in this example, https://preview.acme-corp.com/team—and the exact app bundle ID.

DNS walks one parent at a time and stops at the registrable domain, accounting for public and private suffixes. It never checks _flick.com. A record on a parent must explicitly include the subdomain’s issuer; it is not a wildcard grant.

The first available document or record set is authoritative. Invalid data, a missing matching binding, or a rejected delegation stops verification rather than continuing to another source. Network failures, TLS errors, redirects, and DNS lookup errors also stop discovery. DNS without a responding HTTPS endpoint is not supported by the current implementation.

For a TXT record split into quoted chunks, Flick joins the chunks before decoding. Separate TXT records are decoded separately. Both discovery methods accept arrays with multiple apps or rotation keys.

Configuration

SettingUse
FLICK_ROOT_PRIVATE_KEY_REFRead the root key from another 1Password vault or item
FLICK_ROOT_PRIVATE_KEYSupply the Base58 root private key directly; overrides 1Password
FLICK_PUBLISHING_CREDENTIALPath to the delegate JSON file; --credential overrides it
export FLICK_ROOT_PRIVATE_KEY_REF='op://Engineering/Flick SDK Root Authority/password'

Rotate a root key

Save a new key pair, add its public binding to your discovery document or DNS record, and save the new private key in the 1Password item used by flkr. Remove the old binding when you no longer want to accept publications authorized by it. Previously accepted updates remain available.

Troubleshooting

ProblemCheck
Cannot read the root key1Password CLI is set up and unlocked; the item is in the vault named by the secret reference
No generated SDK project foundRun flkr from the app’s Xcode project directory; use --host to publish from an existing generated contract
Create this view implementation before publishingSelect the view in flkr and press Enter to create its source file
Delegation rejectedThe publisher key matches the grant, the view is included, and the grant has not expired
No discovered root authorizes this publicationCompare the discovery binding’s iss and sub with the scope URL and bundle ID, then check the publisher key, view URI, and expiration in the delegation
Trust-root HTTPS discovery failedCheck the endpoint response: only 404 or 410 allows DNS fallback; connection errors, TLS failures, redirects, and other error statuses stop discovery
No update appearsThe Swift scope and publication use the same trust-root URL