Skip to content

Latest commit

 

History

History
194 lines (115 loc) · 14.9 KB

File metadata and controls

194 lines (115 loc) · 14.9 KB

Wevo

License

Trust, in most services, is stored inside the service itself. Your ratings, your agreements, your track record — they live in someone else's database. When the service shuts down, or changes its rules, or you simply move on, that history disappears with it.

Wevo is an exploration of a different approach. Rather than computing a trust score, Wevo records what happened: proposals made, agreements reached, signatures given. Each event is cryptographically signed by the parties involved, stored locally on your device, and owned by you — not by a platform.

This repository is a Swift reference implementation of that idea, built for iOS and macOS. It is experimental and minimal.

→ For the problem statement, core ideas, and design philosophy behind Wevo, see wevo-space.


What This Repository Is

This is the iOS/macOS client application, built with SwiftUI and SwiftData. It handles:

  • Creating and managing cryptographic identities (P-256, stored in Keychain)
  • Composing, signing, and verifying proposals (Propose)
  • Organizing proposals into spaces (local contexts)
  • Exporting and importing proposals, identities, and contacts via AirDrop and file sharing (.wevo-propose, .wevo-identity, .wevo-contact)
  • Syncing with a WevoSpace server for multi-party coordination, with automatic failover across backup nodes when the primary is unavailable

This is one implementation of the Wevo approach. The underlying ideas — signed proposals, portable identities, locally-owned history — could be implemented on other platforms or in other languages.

Current Status

Experimental / Alpha.

The core flows work: create an identity, write a proposal, sign it, share it. But:

  • No formal data format specification exists yet; the schema may change without migration
  • Automated test coverage is minimal
  • This is not production-ready software

Use it to understand the approach, experiment with the ideas, or contribute to the direction.

Design Notes

  • The P-256 private key is stored in the Keychain (kSecAttrAccessibleAfterFirstUnlock) and is synchronized to iCloud Keychain across the user's devices — it is not device-only, and is included in encrypted device backups. Face ID / Touch ID (LAContext) is required only to export an identity to a file; signing and ordinary key access are not gated by biometrics. See Known Limitations.
  • Signatures use P-256 ECDSA (CryptoKit); public keys are represented as JWK (JSON Web Key) for interoperability with other implementations
  • Public keys are displayed as a short fingerprint (SHA-256 of the raw key bytes, first 8 bytes as colon-separated hex, e.g. AB:CD:EF:12:34:56:78:90) rather than the raw key string
  • Export formats (.wevo-propose, .wevo-identity, .wevo-contact) include a version field to support future format migrations
  • SwiftData with optional CloudKit sync keeps data on-device by default
  • File-based transfer (.wevo-propose, .wevo-identity, .wevo-contact) for peer-to-peer key exchange via AirDrop
  • A Space stores all node URLs for a WevoSpace cluster; on registration the app calls /info on the entered URL to automatically discover peer nodes. All API operations use ResilientProposeAPIClient, which retries across nodes on network errors or 5xx responses and fails immediately on 4xx client errors

Known Limitations

Timestamps are not cryptographically verified

Wevo's cryptographic guarantees cover:

  • What — the content of a Propose, via SHA-256 hash
  • Who — the identity of each signer, via P-256 ECDSA signatures

When is not guaranteed. Timestamps on Proposes are asserted by the client device or the WevoSpace server and are not cryptographically bound to the signatures. A party with a manipulated system clock can create or sign a Propose with an arbitrary timestamp. This also means any future expiry or time-based enforcement mechanism would be gameable by clock manipulation.

The standard remedy is a trusted timestamp authority (RFC 3161), but that would introduce an external party into every signing operation — which conflicts with Wevo's design principle of not depending on platform-owned or third-party infrastructure. This is therefore a known design trade-off, not a bug to fix.

Private key is iCloud-synced and not biometric-gated

The P-256 signing private key is stored with kSecAttrAccessibleAfterFirstUnlock and kSecAttrSynchronizable = true, so it is replicated to iCloud Keychain across the user's devices and included in encrypted device backups — it is not confined to a single device. The Keychain items carry no SecAccessControl, so retrieving the key to sign does not prompt for Face ID / Touch ID; biometric authentication (LAContext) is enforced only on the identity export path.

The practical effect: possession of the account (its iCloud Keychain) is enough to sign as that identity, and device-level biometrics are not a barrier to signing. This favors cross-device continuity and recovery over device-binding, and matches Wevo's "no external infrastructure" stance by relying on the user's own iCloud rather than a dedicated key-management service. Hardening the storage (device-only, biometric-gated signing) is a possible future change; it is documented here so the trade-off is explicit rather than implied.

Getting Started

  1. Open Wevo.xcodeproj in Xcode 15 or later.
  2. Select the Wevo target (iOS or macOS).
  3. Build and run on a simulator or device.

To use the app, add a Space with a WevoSpace server URL from within the app.

CloudKit sync is enabled by default. To disable it, remove cloudKitDatabase: .automatic from the ModelConfiguration in WevoApp.swift.

WevoSpace Server

The companion server handles proposal storage and multi-party synchronization: → wevo-space

When adding a Space, enter the URL of any node in the cluster. The app will automatically discover all peer nodes via the /info endpoint and store them — enabling transparent failover if a node becomes unavailable. Single-node deployments work without any additional configuration.

Try it

TestFlight: https://testflight.apple.com/join/5SacJesr

API / Documentation


If this direction interests you — portable, verifiable history as a foundation for trust — feedback and contributions are welcome.



Wevo(日本語)

多くのサービスでは、信頼に関するデータ――評価、合意の記録、実績――はサービスの内部に閉じ込められています。そのサービスが終了したり、ルールが変わったり、あるいはただ使わなくなったりしたとき、その履歴は消えてしまいます。

Wevo は、そこへの別のアプローチを探るプロジェクトです。信頼をスコアとして計算するのではなく、起きたこと を記録します。提案、合意、署名。それぞれの出来事は関係者によって暗号学的に署名され、自分のデバイスにローカル保存され、プラットフォームではなく自分が所有します。

このリポジトリは、そのアイデアを Swift で実装した reference implementation の一つ です。iOS/macOS 向けです。実験的・最小限の実装です。

→ Wevo の問題提起・コアとなる考え方・設計思想については wevo-space を参照してください。


このリポジトリの位置づけ

これは SwiftUI と SwiftData で構築された iOS/macOS クライアントアプリです。以下を扱います:

  • 暗号学的 Identity の作成・管理(P-256、Keychain に保存)
  • Propose の作成・署名・検証
  • Propose を Space(ローカルコンテキスト)で整理
  • AirDrop やファイル共有による Propose・Identity・Contact のエクスポート/インポート(.wevo-propose.wevo-identity.wevo-contact
  • WevoSpace サーバーとの同期(マルチパーティ連携)、プライマリノードが利用不能な場合はバックアップノードへ自動フェイルオーバー

これは Wevo のアプローチを実装したものの一つです。署名付き提案、ポータブルな Identity、ローカル所有の履歴という考え方は、他のプラットフォームや言語でも実装できます。

現在の状態

実験的 / Alpha。

基本的なフローは動作します:Identity の作成、Propose の作成、署名、共有。ただし:

  • データフォーマットの仕様はまだ確定していません。マイグレーションなしに変更される可能性があります
  • 自動テストのカバレッジは最小限です
  • プロダクション用途には対応していません

アプローチを理解する、アイデアを試してみる、方向性に貢献するために使ってください。

設計メモ

  • P-256 秘密鍵は Keychain(kSecAttrAccessibleAfterFirstUnlock)に保存され、ユーザーの各デバイス間で iCloud Keychain に同期される。デバイス限定ではなく、暗号化されたデバイスバックアップにも含まれる。Face ID / Touch ID(LAContext)は Identity をファイルにエクスポートする時のみ要求され、署名や通常の鍵アクセスは生体認証で保護されない。既知の制限事項を参照
  • P-256 ECDSA 署名(CryptoKit)を使用。公開鍵は JWK(JSON Web Key)形式で表現され、他の実装との相互運用性を確保
  • 公開鍵はUIに生の文字列ではなく、短いフィンガープリント(鍵バイト列の SHA-256 先頭8バイトをコロン区切り16進表示、例: AB:CD:EF:12:34:56:78:90)として表示
  • エクスポートフォーマット(.wevo-propose.wevo-identity.wevo-contact)は将来のフォーマット移行に備えた version フィールドを含む
  • SwiftData とオプションの CloudKit 同期でデータをデバイス上に保持
  • ファイルベースの転送(.wevo-propose.wevo-identity.wevo-contact)でAirDrop経由のP2P鍵交換が可能
  • Space は WevoSpace クラスターの全ノード URL を保持する。登録時に入力 URL の /info を呼び出してピアノードを自動発見する。API 操作はすべて ResilientProposeAPIClient 経由で行われ、ネットワークエラーや 5xx 応答時は次のノードにリトライし、4xx クライアントエラーは即時 throw する

既知の制限事項

タイムスタンプは暗号学的に検証されない

Wevo の暗号学的保証が対象とするのは以下の2点です:

  • What(何を) — Propose の内容(SHA-256 ハッシュによる保証)
  • Who(誰が) — 各署名者の Identity(P-256 ECDSA 署名による保証)

When(いつ) は保証されません。Propose のタイムスタンプはクライアントデバイスまたは WevoSpace サーバーが主張するものであり、署名と暗号学的に紐付いていません。そのため、システムクロックを操作した当事者は任意のタイムスタンプで Propose を作成・署名することができます。将来的に有効期限や時刻ベースの制御を導入した場合も、クロック操作によって回避される可能性があります。

標準的な解決策は信頼できるタイムスタンプ局(RFC 3161)の利用ですが、これはすべての署名操作に外部の第三者を介在させることになり、Wevo の「プラットフォームや外部インフラに依存しない」という設計原則と相容れません。これはバグではなく、設計上の既知のトレードオフです。

秘密鍵は iCloud 同期され、生体認証で保護されない

P-256 署名用の秘密鍵は kSecAttrAccessibleAfterFirstUnlock かつ kSecAttrSynchronizable = true で保存されるため、ユーザーの各デバイス間で iCloud Keychain に複製され、暗号化されたデバイスバックアップにも含まれます。単一デバイスに限定されません。Keychain の項目には SecAccessControl が設定されておらず、署名のために鍵を取得する際に Face ID / Touch ID を要求しません。生体認証(LAContext)は Identity のエクスポート経路でのみ強制されます。

実際上の意味:そのアカウント(の iCloud Keychain)を保有していれば当該 Identity として署名でき、デバイスの生体認証は署名の障壁になりません。これはデバイス束縛よりもデバイス間の継続性・復旧性を優先した設計であり、専用の鍵管理サービスではなくユーザー自身の iCloud に依存することで Wevo の「外部インフラに依存しない」方針とも整合します。保存方法の強化(デバイス限定・署名時の生体認証)は将来的な変更候補であり、トレードオフを暗黙にせず明示するためにここに記載しています。

Getting Started

  1. Xcode 15 以降で Wevo.xcodeproj を開く
  2. Wevo ターゲット(iOS または macOS)を選択
  3. シミュレータまたは実機でビルド・実行

アプリ内で WevoSpace サーバー URL を指定した Space を追加してください。

CloudKit 同期 はデフォルトで有効です。不要な場合は WevoApp.swiftModelConfiguration から cloudKitDatabase: .automatic を削除してください。

WevoSpace サーバー

Propose の保存とマルチパーティ同期を扱うコンパニオンサーバー: → wevo-space

Space を追加するときは、クラスター内のいずれかのノード URL を入力してください。アプリは /info エンドポイント経由でピアノードを自動発見して保存し、ノードが利用不能になった場合に透過的なフェイルオーバーを実現します。シングルノード構成でも追加の設定は不要です。

Try it

TestFlight: https://testflight.apple.com/join/5SacJesr

API / ドキュメント


ポータブルで検証可能な履歴を信頼の基盤にするという方向性に関心があれば、フィードバックや貢献を歓迎します。