Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
739543a
feat: add end-to-end encrypted device sync
debugtheworldbot Jul 14, 2026
bda4c24
fix: configure and stabilize macOS sync UI
debugtheworldbot Jul 14, 2026
36efc71
test: update MSTest async exception assertions
debugtheworldbot Jul 14, 2026
5d599d1
fix(sync): refresh approver device state
debugtheworldbot Jul 15, 2026
2bd6e93
fix(sync): aggregate current device stats
debugtheworldbot Jul 15, 2026
e48dd92
fix(sync): disable rate limits in debug
debugtheworldbot Jul 15, 2026
37045cb
refactor(sync): store credentials in user defaults
debugtheworldbot Jul 15, 2026
f90fbd3
feat(sync): show day-level sync progress
debugtheworldbot Jul 15, 2026
c5923ea
feat(sync): copy recovery code on confirmation
debugtheworldbot Jul 15, 2026
f727785
feat(sync): add Windows day-level progress
debugtheworldbot Jul 15, 2026
fb2f166
refactor(sync): rename recovery copy action
debugtheworldbot Jul 15, 2026
34c1180
fix(sync): disable hourly limits in staging
debugtheworldbot Jul 15, 2026
4eb42d9
docs(agents): require direct staging sync deploys
debugtheworldbot Jul 15, 2026
7616324
fix(sync): localize user-facing errors
debugtheworldbot Jul 15, 2026
edbbba4
fix(sync): address review feedback
debugtheworldbot Jul 15, 2026
f840ebb
test(sync): allow time for manifest pagination
debugtheworldbot Jul 15, 2026
d980a85
feat(sync): add status indicators and help popover
debugtheworldbot Jul 16, 2026
7f798eb
docs(sync): explain synchronized data
debugtheworldbot Jul 16, 2026
ec39b7f
docs: document multi-device sync
debugtheworldbot Jul 16, 2026
c33e824
feat: compare synced and local history trends
debugtheworldbot Jul 16, 2026
9526279
fix: keep unsynced history metrics single-series
debugtheworldbot Jul 16, 2026
0bbc430
docs(sync): explain automatic sync timing
debugtheworldbot Jul 17, 2026
3b041c8
fix(sync): widen help popover
debugtheworldbot Jul 17, 2026
8d7a59d
fix(sync): size help popover content view
debugtheworldbot Jul 17, 2026
b5b42ef
fix: configure staging sync URL for debug builds
debugtheworldbot Jul 18, 2026
2aaa5ee
fix: resolve sync nullable warnings
debugtheworldbot Jul 18, 2026
5953c55
feat: align sync status UI with macOS
debugtheworldbot Jul 18, 2026
293c91e
feat: add local history trend line
debugtheworldbot Jul 18, 2026
c889078
style: modernize shared Windows controls
debugtheworldbot Jul 18, 2026
38bdab3
style: align sync destructive actions inline
debugtheworldbot Jul 18, 2026
33468a6
fix: align sync status indicator
debugtheworldbot Jul 18, 2026
a6348b8
fix: bypass manual sync rate limit in debug
debugtheworldbot Jul 18, 2026
b9085fe
fix: reduce sync status dot size
debugtheworldbot Jul 18, 2026
30d560f
fix(sync): rebuild history after cache rollback
debugtheworldbot Jul 18, 2026
463ac0e
fix(sync): report staging quota as available
debugtheworldbot Jul 18, 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
19 changes: 16 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:
# ============================================================
build-macos:
runs-on: macos-26
env:
KEYSTATS_SYNC_SERVICE_URL: ${{ vars.KEYSTATS_SYNC_PRODUCTION_URL }}
outputs:
version: ${{ steps.version.outputs.VERSION }}
sha256: ${{ steps.sha256.outputs.SHA256 }}
Expand Down Expand Up @@ -46,7 +48,12 @@ jobs:
run: ./scripts/check_vendored_helper.sh

- name: Build DMG
run: ./scripts/build_dmg.sh
run: |
if [[ ! "$KEYSTATS_SYNC_SERVICE_URL" =~ ^https://[A-Za-z0-9.-]+\.workers\.dev/?$ ]]; then
echo "KEYSTATS_SYNC_PRODUCTION_URL must be a production workers.dev URL."
exit 1
fi
./scripts/build_dmg.sh

- name: Validate Sparkle private key
env:
Expand Down Expand Up @@ -93,7 +100,8 @@ jobs:
archive \
CODE_SIGN_IDENTITY="-" \
ARCHS="arm64 x86_64" \
ONLY_ACTIVE_ARCH=NO
ONLY_ACTIVE_ARCH=NO \
KEYSTATS_SYNC_SERVICE_URL="$KEYSTATS_SYNC_SERVICE_URL"

APP_PATH="$ARCHIVE_PATH/Products/Applications/$APP_NAME.app"
cp -R "$APP_PATH" "$STAGING_DIR/"
Expand Down Expand Up @@ -139,6 +147,8 @@ jobs:
# ============================================================
build-windows:
runs-on: windows-latest
env:
KEYSTATS_SYNC_SERVICE_URL: ${{ vars.KEYSTATS_SYNC_PRODUCTION_URL }}
outputs:
version: ${{ steps.version.outputs.VERSION }}

Expand Down Expand Up @@ -168,7 +178,10 @@ jobs:
shell: pwsh
working-directory: KeyStats.Windows
run: |
.\build.ps1 -Configuration Release
if ($env:KEYSTATS_SYNC_SERVICE_URL -notmatch '^https://[A-Za-z0-9.-]+\.workers\.dev/?$') {
throw 'KEYSTATS_SYNC_PRODUCTION_URL must be a production workers.dev URL.'
}
.\build.ps1 -Configuration Release -SyncServiceBaseUrl $env:KEYSTATS_SYNC_SERVICE_URL

- name: Upload Windows artifact
uses: actions/upload-artifact@v4
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/sync-worker-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Sync Worker CI

on:
pull_request:
paths:
- 'contracts/sync/v1/**'
- 'services/sync-worker/**'
- '.github/workflows/sync-worker-*.yml'
push:
branches-ignore: [main]
paths:
- 'contracts/sync/v1/**'
- 'services/sync-worker/**'
- '.github/workflows/sync-worker-*.yml'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: sync-worker-ci-${{ github.ref }}
cancel-in-progress: true

defaults:
run:
working-directory: services/sync-worker

jobs:
validate:
name: Contract, migration, type and Worker tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: services/sync-worker/package-lock.json
- run: npm ci
- name: Validate schemas, OpenAPI and golden vectors
run: npm run contracts:check
- name: Apply D1 migrations to an isolated local database
run: npx wrangler d1 migrations apply DB --local
- name: Type-check Worker
run: npm run types:check && npm run typecheck
- name: Run Miniflare and unit tests
run: npm test
68 changes: 68 additions & 0 deletions .github/workflows/sync-worker-deploy-production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Deploy Sync Worker Production

on:
workflow_dispatch:
inputs:
confirmation:
description: 'Type deploy-production after staging verification'
required: true
type: string

permissions:
contents: read

concurrency:
group: sync-worker-production
cancel-in-progress: false

defaults:
run:
working-directory: services/sync-worker

jobs:
authorize:
name: Validate explicit confirmation
runs-on: ubuntu-latest
steps:
- name: Require exact production confirmation
if: ${{ inputs.confirmation != 'deploy-production' }}
run: exit 1

deploy:
name: Validate and deploy production
needs: authorize
runs-on: ubuntu-latest
timeout-minutes: 20
environment: sync-production
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_D1_DATABASE_ID: ${{ vars.CLOUDFLARE_D1_DATABASE_ID }}
TOKEN_HASH_KEY: ${{ secrets.TOKEN_HASH_KEY }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: services/sync-worker/package-lock.json
- run: npm ci
- run: npm run check
- name: Validate deployment secrets
run: |
if [ -z "$CLOUDFLARE_API_TOKEN" ] || [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then
echo "Cloudflare deployment credentials are not configured."
exit 1
fi
if [ "${#TOKEN_HASH_KEY}" -lt 32 ]; then
echo "TOKEN_HASH_KEY must contain at least 32 characters."
exit 1
fi
- name: Prepare bound production configuration
run: node scripts/write-deploy-config.mjs production wrangler.deploy.json
- name: Apply production D1 migrations
run: npx wrangler d1 migrations apply DB --env production --remote --config wrangler.deploy.json
- name: Configure token hashing secret
run: printf '%s' "$TOKEN_HASH_KEY" | npx wrangler secret put TOKEN_HASH_KEY --env production --config wrangler.deploy.json
- name: Deploy production Worker
run: npx wrangler deploy --env production --config wrangler.deploy.json
59 changes: 59 additions & 0 deletions .github/workflows/sync-worker-deploy-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Deploy Sync Worker Staging

on:
push:
branches: [main]
paths:
- 'contracts/sync/v1/**'
- 'services/sync-worker/**'
- '.github/workflows/sync-worker-*.yml'

permissions:
contents: read

concurrency:
group: sync-worker-staging
cancel-in-progress: false

defaults:
run:
working-directory: services/sync-worker

jobs:
deploy:
name: Validate and deploy staging
runs-on: ubuntu-latest
timeout-minutes: 20
environment: sync-staging
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_D1_DATABASE_ID: ${{ vars.CLOUDFLARE_D1_DATABASE_ID }}
TOKEN_HASH_KEY: ${{ secrets.TOKEN_HASH_KEY }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: services/sync-worker/package-lock.json
- run: npm ci
- run: npm run check
- name: Validate deployment secrets
run: |
if [ -z "$CLOUDFLARE_API_TOKEN" ] || [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then
echo "Cloudflare deployment credentials are not configured."
exit 1
fi
if [ "${#TOKEN_HASH_KEY}" -lt 32 ]; then
echo "TOKEN_HASH_KEY must contain at least 32 characters."
exit 1
fi
- name: Prepare bound staging configuration
run: node scripts/write-deploy-config.mjs staging wrangler.deploy.json
- name: Apply staging D1 migrations
run: npx wrangler d1 migrations apply DB --env staging --remote --config wrangler.deploy.json
- name: Configure token hashing secret
run: printf '%s' "$TOKEN_HASH_KEY" | npx wrangler secret put TOKEN_HASH_KEY --env staging --config wrangler.deploy.json
- name: Deploy staging Worker
run: npx wrangler deploy --env staging --config wrangler.deploy.json
35 changes: 35 additions & 0 deletions .github/workflows/windows-sync-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Windows Sync Tests

on:
pull_request:
paths:
- 'KeyStats.Windows/**'
- 'contracts/sync/v1/**'
- '.github/workflows/windows-sync-tests.yml'
push:
branches: [main]
paths:
- 'KeyStats.Windows/**'
- 'contracts/sync/v1/**'
- '.github/workflows/windows-sync-tests.yml'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: windows-sync-tests-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: .NET Framework sync tests
runs-on: windows-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Run Windows sync tests
run: dotnet test KeyStats.Windows/KeyStats.Sync.Tests/KeyStats.Sync.Tests.csproj --configuration Release --verbosity normal
13 changes: 13 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ Issue type?
- ✅ Implement Codable for data structures needing persistence
- ✅ Batch UI updates to reduce main thread blocking

### 🟣 Automatic Commit Policy

- ✅ After a feature or module is fully implemented and its relevant checks pass, automatically create an atomic Git commit without waiting for an additional user request
- ✅ Before committing, inspect `git status` again and include only files changed for the completed feature or module
- ✅ Do not automatically commit incomplete work, failed verification, or unrelated existing changes
- ✅ Follow the repository's commit message and Git safety rules for every automatic commit

### ☁️ Sync Worker Staging Deployment

- ✅ After changing the staging sync service (`services/sync-worker/**`, `contracts/sync/v1/**`, or its staging workflow) and passing the relevant checks, deploy the staging Worker directly without waiting for additional confirmation
- ✅ Apply pending staging D1 migrations before deploying, and always target the explicit `staging` Wrangler environment
- ✅ Never deploy the production Worker without explicit user authorization in the current conversation

---

## Build & Development Commands
Expand Down
3 changes: 3 additions & 0 deletions KeyStats.Windows/KeyStats.Sync.Tests/AssemblyAttributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

[assembly: DoNotParallelize]
28 changes: 28 additions & 0 deletions KeyStats.Windows/KeyStats.Sync.Tests/KeyStats.Sync.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Include="MSTest.TestAdapter" Version="4.3.0" />
<PackageReference Include="MSTest.TestFramework" Version="4.3.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\KeyStats\KeyStats.csproj" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\contracts\sync\v1\fixtures\crypto-vectors.json"
Link="Fixtures\crypto-vectors.json"
CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

</Project>
Loading
Loading