Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/acceptance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ concurrency:
cancel-in-progress: true

env:
RIVET_PIN: 1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
RIVET_PIN: 288c81f547eb64ec043546acc4df07efc19aceec
KEEL_PIN: 0740fdfbdab7bf5dab32232c82f07dd98aa6a196

jobs:
Expand Down Expand Up @@ -102,7 +102,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up embeddable Racket CS
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@288c81f547eb64ec043546acc4df07efc19aceec
- name: Install native and pinned Racket dependencies
shell: bash
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ concurrency:
# Single source of truth for the rivet pin; every install step below
# references this so a bump is one edit, not a grep-replace across jobs.
env:
RIVET_PIN: 1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
RIVET_PIN: 288c81f547eb64ec043546acc4df07efc19aceec
jobs:
contract:
name: Contract preflight (i18n + release metadata)
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
# Builds and caches the embeddable Racket CS runtime and exports
# RIVET_RACKET_* (rivet's own composite action).
- name: Set up embeddable Racket CS
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@288c81f547eb64ec043546acc4df07efc19aceec
# Installed after the embeddable runtime so the catalog dependency
# closure (cext-lib, crypto-lib, keel) resolves against the minimal
# build — it ships no packages of its own. db backs taskly's storage;
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-version-client-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ concurrency:
cancel-in-progress: false

env:
RIVET_PIN: 1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
RIVET_PIN: 288c81f547eb64ec043546acc4df07efc19aceec
KEEL_PIN: 0740fdfbdab7bf5dab32232c82f07dd98aa6a196

jobs:
Expand Down Expand Up @@ -48,7 +48,7 @@ jobs:
libsecret-1-dev libglib2.0-dev

- name: Set up embeddable Racket CS
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@288c81f547eb64ec043546acc4df07efc19aceec

- name: Install pinned Racket dependencies
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ concurrency:
# Single source of truth for the rivet pin; every install step below
# references this so a bump is one edit, not a grep-replace across jobs.
env:
RIVET_PIN: 1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
RIVET_PIN: 288c81f547eb64ec043546acc4df07efc19aceec
jobs:
macos:
name: macOS (${{ matrix.arch }})
Expand Down Expand Up @@ -151,7 +151,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up embeddable Racket CS
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@1ebb3ac09ed2e23eeb0a91cbec6e689aac01ab1a
uses: turinglambdaai/rivet/.github/actions/setup-embed-racket@288c81f547eb64ec043546acc4df07efc19aceec
# Installed after the embeddable runtime so the catalog dependency
# closure (cext-lib, crypto-lib, keel) resolves against the minimal
# build — it ships no packages of its own. db backs taskly's storage;
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Network transparency: the macOS bundle ships an
`InternetAccessPolicy.plist` declaring the update feed as the app's
only network use — firewall apps (Little Snitch and friends) show the
policy's per-host purpose and deny consequences to the user, and
denying it leaves Taskly fully functional offline (#58).
- About dialog becomes the update/link/feedback portal on every platform:
User Manual and Changelog links join Website and Report an Issue, and a
diagnostics block (release identity, platform, database and config
Expand Down
27 changes: 27 additions & 0 deletions assets/InternetAccessPolicy.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ApplicationDescription</key>
<string>Taskly is a local-first task manager: every task lives in one SQLite file on this Mac. It ships no telemetry and no account connections — its only network use is checking for and downloading application updates.</string>
<key>Connections</key>
<array>
<dict>
<key>Host</key>
<string>github.com, *.github.com, objects.githubusercontent.com, *.objects.githubusercontent.com</string>
<key>IsIncoming</key>
<false/>
<key>NetworkProtocol</key>
<string>TCP</string>
<key>Port</key>
<string>443</string>
<key>Purpose</key>
<string>Taskly checks for new versions and downloads application updates: the signed update manifest and the update package itself.</string>
<key>DenyConsequences</key>
<string>If you deny these connections, Taskly keeps working fully offline — it just cannot check for or install updates.</string>
<key>Relevance</key>
<string>Essential</string>
</dict>
</array>
</dict>
</plist>
34 changes: 32 additions & 2 deletions macos-host/Sources/RivetHost/GeneratedBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,39 @@ public enum RivetEvent: Sendable {
}
}

public protocol RivetGeneratedRPCClient: Sendable {
func call(_ name: String, arguments: [RivetValue]) async throws -> RivetValue
}

// Keep generated clients compatible with the last released SwiftPM runtime.
// Authenticated network clients can use RivetAPI(call: deviceClient.call).
public extension RivetGeneratedRPCClient {
func getState(_ name: String) async throws -> RivetValue {
try await call("$state/get", arguments: [.string(name)])
}

@discardableResult
func setState(_ name: String, value: RivetValue) async throws -> RivetValue {
try await call("$state/set", arguments: [.string(name), value])
}
}

extension RivetClient: RivetGeneratedRPCClient {}

private struct RivetGeneratedClosureClient: RivetGeneratedRPCClient {
let invoke: @Sendable (String, [RivetValue]) async throws -> RivetValue

func call(_ name: String, arguments: [RivetValue]) async throws -> RivetValue {
try await invoke(name, arguments)
}
}

public struct RivetAPI: Sendable {
public let client: RivetClient
public init(client: RivetClient) { self.client = client }
public let client: any RivetGeneratedRPCClient
public init(client: any RivetGeneratedRPCClient) { self.client = client }
public init(call: @escaping @Sendable (String, [RivetValue]) async throws -> RivetValue) {
self.client = RivetGeneratedClosureClient(invoke: call)
}

public func add_task(text: String, list_id: Int64?, due_date: String?, due_time: String?, notes: String?) async throws -> RivetTypes.Task {
let rivetResult = try await client.call("add_task", arguments: [encode_String(text), encode__Optional_Int64_(list_id), encode__Optional_String_(due_date), encode__Optional_String_(due_time), encode__Optional_String_(notes)])
Expand Down
2 changes: 1 addition & 1 deletion rivet.rktd
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#hasheq((name . "taskly") (display-name . "Taskly") (version . "0.2.4") (build . 1) (identifier . "app.taskly.Taskly") (macos-min-version . "14.0") (windows-min-version . "10.0.19041.0") (backend . "racket/taskly/backend.rkt") (module . "backend") (entry . "start") (protocol . 1) (windows-icon . "assets/taskly.ico") (macos-icon . "assets/taskly.icns") (dmg-background . "assets/dmg-background.png") (windows-installer-bitmaps . #hasheq((banner . "assets/wizard-banner.bmp") (dialog . "assets/wizard-dialog.bmp"))) (linux-icon . "assets/icon_512.png") (linux-formats . ("deb" "rpm" "appimage")) (resources . ("shared/i18n" "shared/emoji.json")))
#hasheq((name . "taskly") (display-name . "Taskly") (version . "0.2.4") (build . 1) (identifier . "app.taskly.Taskly") (macos-min-version . "14.0") (windows-min-version . "10.0.19041.0") (backend . "racket/taskly/backend.rkt") (module . "backend") (entry . "start") (protocol . 1) (windows-icon . "assets/taskly.ico") (macos-icon . "assets/taskly.icns") (dmg-background . "assets/dmg-background.png") (windows-installer-bitmaps . #hasheq((banner . "assets/wizard-banner.bmp") (dialog . "assets/wizard-dialog.bmp"))) (macos-resource-files . ("assets/InternetAccessPolicy.plist")) (linux-icon . "assets/icon_512.png") (linux-formats . ("deb" "rpm" "appimage")) (resources . ("shared/i18n" "shared/emoji.json")))
10 changes: 10 additions & 0 deletions shared/spec/UPDATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ backend boundary every host shares:
and packaged-acceptance jobs; rollback and manual-recovery notes per
install shape live in the lifecycle section of this document.

## Network transparency

The update feed is the application's only network use: Taskly ships no
telemetry, accounts, or any other connection. The macOS bundle carries
an `InternetAccessPolicy.plist` (packaged via rivet's
`macos-resource-files`) declaring the `github.com` feed hosts, their
purpose, and the deny consequences — firewall apps such as Little
Snitch surface that policy to the user verbatim. Denying the feed
leaves every other feature fully functional offline.

## Ed25519 signature

- Key type: Ed25519. Private key lives ONLY in the GitHub secret
Expand Down
Loading