Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
eb9d2e5
Updating BushelCloud Subrepo (#195)
leogdion Jan 8, 2026
e650441
Fix QueryFilter IN/NOT_IN serialization to preserve type information …
leogdion Jan 9, 2026
99d3285
Add demo-in-filter command and fix MistDemo auth model
leogdion Apr 14, 2026
cc3f60c
Fix IN/NOT_IN filter by adding required CloudKit list type field
leogdion Apr 14, 2026
a084e00
git subrepo push Examples/BushelCloud
leogdion Apr 14, 2026
ecce30d
git subrepo push Examples/CelestraCloud
leogdion Apr 14, 2026
60b9869
Add setup-mistkit composite action to BushelCloud and CelestraCloud
leogdion Apr 14, 2026
4482a48
Switch example workflows to MistKit branch 192-query-filter-in
leogdion Apr 14, 2026
9472950
Move MistKit branch to action default, remove from workflow call sites
leogdion Apr 14, 2026
2dafb14
git subrepo push Examples/BushelCloud
leogdion Apr 14, 2026
bd9635d
git subrepo push Examples/CelestraCloud
leogdion Apr 14, 2026
833f2ff
Run setup-mistkit before fallback build in cloudkit-sync action
leogdion Apr 14, 2026
c85fde5
git subrepo push Examples/BushelCloud
leogdion Apr 14, 2026
eba9065
Remove stale workflow_run trigger from cloudkit-sync-dev
leogdion Apr 14, 2026
5d3e331
git subrepo push Examples/BushelCloud
leogdion Apr 14, 2026
962a560
Handle duplicate GUIDs in CloudKit data gracefully
leogdion Apr 14, 2026
9d8729b
git subrepo push Examples/CelestraCloud
leogdion Apr 14, 2026
10c4088
Assert listType in IN/NOT_IN filter tests
leogdion Apr 14, 2026
e8a3a19
Address PR #205 review comments
leogdion Apr 15, 2026
eb49e88
git subrepo push Examples/CelestraCloud
leogdion Apr 15, 2026
401cf27
undoing some file changes
leogdion Apr 15, 2026
706f476
Revert CLOUDKIT_WEB_AUTH_TOKEN → CLOUDKIT_WEBAUTH_TOKEN
leogdion Apr 15, 2026
539895a
Revert "Revert CLOUDKIT_WEB_AUTH_TOKEN → CLOUDKIT_WEBAUTH_TOKEN"
leogdion Apr 15, 2026
2e99f9f
Fix token expiration boundary check to treat exact-time expiry as exp…
leogdion Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/docs/mistdemo/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ All configuration keys can be set via environment variables.
| `api_token` | `CLOUDKIT_API_TOKEN` |
| `environment` | `CLOUDKIT_ENVIRONMENT` |
| `database` | `CLOUDKIT_DATABASE` |
| `web_auth_token` | `CLOUDKIT_WEBAUTH_TOKEN` |
| `web_auth_token` | `CLOUDKIT_WEB_AUTH_TOKEN` |
| `key_id` | `CLOUDKIT_KEY_ID` |
| `private_key_file` | `CLOUDKIT_PRIVATE_KEY_FILE` |
| `output` | `MISTDEMO_OUTPUT` |
Expand Down
4 changes: 2 additions & 2 deletions .claude/docs/mistdemo/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ query_with_auth_retry() {

if [ "$code" = "AUTHENTICATION_FAILED" ]; then
echo "Token expired, re-authenticating..." >&2
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")

# Retry
mistdemo query --database private
Expand All @@ -469,7 +469,7 @@ Error: Invalid or expired web authentication token
**Solution:**
```bash
# Get new token
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")

# Retry operation
mistdemo query --database private
Expand Down
98 changes: 43 additions & 55 deletions .claude/docs/mistdemo/operations-auth.md
Comment thread
leogdion marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,25 @@ Authentication operations handle obtaining and validating CloudKit authenticatio

### Authentication Methods

CloudKit Web Services supports two authentication methods:

#### 1. Web Authentication Token (User Authentication)
- **Use Case**: User-specific operations in private/shared databases
- **Flow**: OAuth-style browser-based authentication
- **Token Type**: Web auth token (user-scoped)
#### 1. Web Authentication Token — Private/Shared Database
- **Use Case**: Private and shared database operations
- **Credentials**: `CLOUDKIT_API_TOKEN` + `CLOUDKIT_WEB_AUTH_TOKEN`
- **Flow**: OAuth-style browser-based sign-in (`mistdemo auth-token`)
- **Duration**: Temporary (expires periodically)
- **Required For**: Private and shared database access

#### 2. Server-to-Server Key (Server Authentication)
- **Use Case**: Server-side applications, automation
- **Flow**: ECDSA key-based signing
- **Token Type**: None (signs requests directly)
#### 2. Server-to-Server Key — Public Database
- **Use Case**: Public database operations
- **Credentials**: `CLOUDKIT_KEY_ID` + `CLOUDKIT_PRIVATE_KEY` or `CLOUDKIT_PRIVATE_KEY_PATH`
- **Flow**: ECDSA request signing (no browser required)
- **Duration**: Permanent (until key is revoked)
- **Required For**: Server automation, background jobs

### Database Access Requirements

| Database | Public Operations | Authenticated Operations |
|----------|------------------|--------------------------|
| **Public** | API token only | API token only |
| **Private** | Not allowed | Web auth token or server key |
| **Shared** | Not allowed | Web auth token or server key |
| Database | Required Credentials | Auth Method |
|----------|---------------------|-------------|
| **Public** | `CLOUDKIT_KEY_ID` + `CLOUDKIT_PRIVATE_KEY[_FILE]` | Server-to-server signing |
| **Private** | `CLOUDKIT_API_TOKEN` + `CLOUDKIT_WEB_AUTH_TOKEN` | Web authentication |
| **Shared** | `CLOUDKIT_API_TOKEN` + `CLOUDKIT_WEB_AUTH_TOKEN` | Web authentication |

## auth-token

Expand Down Expand Up @@ -71,7 +67,7 @@ mistdemo auth-token --api-token YOUR_API_TOKEN

**Save to environment variable:**
```bash
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token --api-token YOUR_API_TOKEN)
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token --api-token YOUR_API_TOKEN)
```

**Save to file:**
Expand Down Expand Up @@ -145,7 +141,7 @@ Waiting for authentication callback...

# Check if token exists and is valid
if [ -f ~/.mistdemo/token.txt ]; then
export CLOUDKIT_WEBAUTH_TOKEN=$(cat ~/.mistdemo/token.txt)
export CLOUDKIT_WEB_AUTH_TOKEN=$(cat ~/.mistdemo/token.txt)
if mistdemo validate > /dev/null 2>&1; then
echo "Using existing token"
exit 0
Expand All @@ -156,15 +152,15 @@ fi
echo "Obtaining new authentication token..."
mistdemo auth-token --api-token "$CLOUDKIT_API_TOKEN" > ~/.mistdemo/token.txt
chmod 600 ~/.mistdemo/token.txt
export CLOUDKIT_WEBAUTH_TOKEN=$(cat ~/.mistdemo/token.txt)
export CLOUDKIT_WEB_AUTH_TOKEN=$(cat ~/.mistdemo/token.txt)
echo "Authentication complete"
```

**CI/CD automation:**
```bash
# For server-to-server authentication
export CLOUDKIT_KEY_ID="your-key-id"
export CLOUDKIT_PRIVATE_KEY_FILE="/path/to/private-key.pem"
export CLOUDKIT_PRIVATE_KEY_PATH="/path/to/private-key.pem"

# No web auth token needed
mistdemo query --database private
Expand All @@ -173,7 +169,7 @@ mistdemo query --database private
**Interactive session:**
```bash
# One-time setup per session
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)

# Use for all subsequent commands
mistdemo query --database private
Expand Down Expand Up @@ -270,7 +266,7 @@ fi

mistdemo validate --test-query || {
echo "Error: Invalid authentication. Please re-authenticate."
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")
}

# Proceed with operations
Expand All @@ -295,56 +291,48 @@ done

## Authentication Workflows

### First-Time Setup
### First-Time Setup — Public Database

```bash
# 1. Set API token (from CloudKit Dashboard)
export CLOUDKIT_API_TOKEN="your-api-token-here"
# 1. Set container ID
export CLOUDKIT_CONTAINER_IDENTIFIER="iCloud.com.example.MyApp"

# 2. Set container ID
export CLOUDKIT_CONTAINER_ID="iCloud.com.example.MyApp"
# 2. Set server-to-server credentials (from CloudKit Dashboard)
export CLOUDKIT_KEY_ID="your-key-id"
export CLOUDKIT_PRIVATE_KEY_PATH="/path/to/eckey.pem"

# 3. Test public database access
mistdemo query --database public

# 4. Get web auth token for private access
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")

# 5. Verify private access
mistdemo current-user --database private
mistdemo query
```

### Development Environment
### First-Time Setup — Private Database

```bash
# .env file (never commit this)
CLOUDKIT_CONTAINER_ID=iCloud.com.example.MyApp
CLOUDKIT_API_TOKEN=your-api-token
CLOUDKIT_ENVIRONMENT=development
CLOUDKIT_DATABASE=private
# 1. Set container ID
export CLOUDKIT_CONTAINER_IDENTIFIER="iCloud.com.example.MyApp"

# Load environment
source .env
# 2. Set API token (from CloudKit Dashboard)
export CLOUDKIT_API_TOKEN="your-api-token"

# 3. Get web auth token (browser sign-in)
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token)

# Get token for session
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")
# 4. Verify private access
mistdemo current-user
```

### Production Environment (Server-to-Server)
### Automated / CI Environment (Public Database)

```bash
# Generate key pair (one time)
# See CloudKit documentation for key generation

# Set environment variables
export CLOUDKIT_CONTAINER_ID=iCloud.com.example.MyApp
export CLOUDKIT_API_TOKEN=your-api-token
export CLOUDKIT_CONTAINER_IDENTIFIER=iCloud.com.example.MyApp
export CLOUDKIT_ENVIRONMENT=production
export CLOUDKIT_KEY_ID=your-key-id
export CLOUDKIT_PRIVATE_KEY_FILE=/secure/path/to/key.pem
export CLOUDKIT_PRIVATE_KEY_PATH=/secure/path/to/key.pem

# No web auth needed
mistdemo query --database private
# Run public database operations — no browser required
mistdemo query
mistdemo demo-in-filter
```

## Troubleshooting
Expand All @@ -353,7 +341,7 @@ mistdemo query --database private
```bash
# Error: AUTHENTICATION_FAILED
# Solution: Get new token
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a "$CLOUDKIT_API_TOKEN")
```

### Port Already in Use
Expand Down
4 changes: 2 additions & 2 deletions .claude/docs/mistdemo/operations-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ mistdemo current-user --fields "userRecordName,firstName,lastName"
**With authentication:**
```bash
# Get web auth token first
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)

# Query current user
mistdemo current-user --database private
Expand Down Expand Up @@ -294,7 +294,7 @@ mistdemo lookup-contacts \
### Verify Authentication and Get User Info
```bash
# Set up authentication
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)

# Get current user
mistdemo current-user --database private -o table
Expand Down
42 changes: 27 additions & 15 deletions .claude/docs/mistdemo/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ All subcommands accept these global options:

| Option | Short | Environment Variable | Description |
|--------|-------|---------------------|-------------|
| `--container-id` | `-c` | `CLOUDKIT_CONTAINER_ID` | CloudKit container identifier |
| `--api-token` | `-a` | `CLOUDKIT_API_TOKEN` | CloudKit API token |
| `--container-id` | `-c` | `CLOUDKIT_CONTAINER_IDENTIFIER` | CloudKit container identifier |

### Database & Environment

Expand All @@ -50,11 +49,20 @@ Valid values:

### Authentication Options

**Public database** — server-to-server signing:

| Option | Environment Variable | Description |
|--------|---------------------|-------------|
| `--key-id` | `CLOUDKIT_KEY_ID` | Server-to-server key ID (required for public database) |
| `--private-key-file` | `CLOUDKIT_PRIVATE_KEY_PATH` | Path to ECDSA private key PEM file |
| `--private-key` | `CLOUDKIT_PRIVATE_KEY` | ECDSA private key as inline string |

**Private/shared database** — web authentication:

| Option | Environment Variable | Description |
|--------|---------------------|-------------|
| `--web-auth-token` | `CLOUDKIT_WEBAUTH_TOKEN` | Web authentication token (required for private/shared databases) |
| `--key-id` | `CLOUDKIT_KEY_ID` | Server-to-server key ID |
| `--private-key-file` | `CLOUDKIT_PRIVATE_KEY_FILE` | Path to server-to-server private key |
| `--api-token` | `CLOUDKIT_API_TOKEN` | CloudKit API token (required for private/shared database) |
| `--web-auth-token` | `CLOUDKIT_WEB_AUTH_TOKEN` | Web authentication token (from `mistdemo auth-token`) |

### Output Options

Expand All @@ -81,22 +89,26 @@ Valid output formats: `json`, `table`, `csv`, `yaml`

## Authentication Overview

MistDemo supports two authentication methods:

### 1. Web Auth Token (User Authentication)
Required for accessing private or shared databases.
MistDemo uses different credentials depending on the target database:

### Public Database — Server-to-Server Key
```bash
# Obtain token
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token --api-token YOUR_API_TOKEN)
export CLOUDKIT_KEY_ID="your-key-id"
export CLOUDKIT_PRIVATE_KEY_PATH="/path/to/eckey.pem"
mistdemo query
mistdemo demo-in-filter
```

# Use token in commands
mistdemo query --database private
### Private/Shared Database — Web Authentication
```bash
export CLOUDKIT_API_TOKEN="your-api-token"
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token)
mistdemo current-user
```

See [Authentication Operations](operations-auth.md) for details.

### 2. Server-to-Server Key (Server Authentication)
### Legacy: Server-to-Server Key (Private Database)
For server-side applications using key-based authentication.

```bash
Expand Down Expand Up @@ -215,7 +227,7 @@ mistdemo create \
### Authenticated Operations
```bash
# Get auth token first
export CLOUDKIT_WEBAUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)
export CLOUDKIT_WEB_AUTH_TOKEN=$(mistdemo auth-token -a YOUR_API_TOKEN)

# Use private database
mistdemo query --database private
Expand Down
10 changes: 0 additions & 10 deletions .env.example
Comment thread
leogdion marked this conversation as resolved.

This file was deleted.

26 changes: 26 additions & 0 deletions .github/actions/setup-mistkit/action.yml
Comment thread
leogdion marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Setup MistKit
description: Replaces the local MistKit path dependency with a remote branch reference

inputs:
branch:
description: MistKit branch to use (leave empty to keep the local path dependency)

runs:
using: composite
steps:
- name: Update Package.swift (Unix)
if: inputs.branch != '' && runner.os != 'Windows'
shell: bash
run: |
if [ "$RUNNER_OS" = "macOS" ]; then
sed -i '' 's|\.package(name: "MistKit", path: "\.\./\.\.")|.package(url: "https://github.com/brightdigit/MistKit.git", branch: "'"${{ inputs.branch }}"'")|g' Package.swift
else
sed -i 's|\.package(name: "MistKit", path: "\.\./\.\.")|.package(url: "https://github.com/brightdigit/MistKit.git", branch: "'"${{ inputs.branch }}"'")|g' Package.swift
fi
rm -f Package.resolved
- name: Update Package.swift (Windows)
if: inputs.branch != '' && runner.os == 'Windows'
shell: pwsh
run: |
(Get-Content Package.swift) -replace '\.package\(name: "MistKit", path: "\.\./\.\."\)', ".package(url: `"https://github.com/brightdigit/MistKit.git`", branch: `"${{ inputs.branch }}`")" | Set-Content Package.swift
Remove-Item -Path Package.resolved -Force -ErrorAction SilentlyContinue
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ A `ClientTransport` extension could provide a generic upload method, but would n

### CloudKit Web Services Integration
- Base URL: `https://api.apple-cloudkit.com`
- Authentication: API Token + Web Auth Token or Server-to-Server Key Authentication
- Authentication:
- **Public database**: `CLOUDKIT_KEY_ID` + `CLOUDKIT_PRIVATE_KEY` or `CLOUDKIT_PRIVATE_KEY_PATH` → server-to-server signing
- **Private database**: `CLOUDKIT_API_TOKEN` + `CLOUDKIT_WEB_AUTH_TOKEN` → web authentication
- All operations should reference the OpenAPI spec in `cloudkit-api-openapi.yaml`
- URL Pattern: `/database/{version}/{container}/{environment}/{database}/{operation}`
- Supported databases: `public`, `private`, `shared`
Expand Down Expand Up @@ -262,6 +264,8 @@ A `ClientTransport` extension could provide a generic upload method, but would n

The Swift package uses Apple's swift-openapi-generator to create type-safe client code from the OpenAPI specification. Generated code is placed in `Sources/MistKit/Generated/` and should not be committed to version control.

> **IMPORTANT: Never manually edit files in `Sources/MistKit/Generated/`.** These files are auto-generated from `openapi.yaml`. Any manual edits will be lost when code is regenerated. Instead, modify `openapi.yaml` and regenerate using `./Scripts/generate-openapi.sh`.

The `openapi.yaml` file serves as the source of truth for:
- All available endpoints and their HTTP methods
- Request/response schemas and models
Expand Down
4 changes: 4 additions & 0 deletions Examples/BushelCloud/.github/actions/cloudkit-sync/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ runs:
path: ./binary
branch: ${{ github.ref_name }}

- name: Setup MistKit
if: steps.download-binary.outcome != 'success'
uses: ./.github/actions/setup-mistkit

- name: Build binary (fallback if artifact unavailable)
if: steps.download-binary.outcome != 'success'
shell: bash
Expand Down
Loading
Loading