Skip to content

fix: release readiness — sessions survive outages, Keychain-only tokens, real script timeouts - #2

Merged
CLCK0622 merged 3 commits into
mainfrom
fix/release-readiness
Sep 23, 2026
Merged

CLCK0622 merged 3 commits into
mainfrom
fix/release-readiness

Conversation

@CLCK0622

@CLCK0622 CLCK0622 commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Why

A pre-release audit found issues that should be fixed before a stable v0.2.4:

  • A brief network or server failure at launch signed the user out and revoked the session on the server.
  • Launching offline showed a splash screen that spun forever.
  • Release builds silently wrote tokens to a plaintext file whenever the Keychain refused a write, which contradicts the privacy policy.
  • The weather location prompt probably failed under the hardened runtime, because the location entitlement was missing.
  • AppleScript timeouts never took effect.
  • Attachments were read on the main thread with no size limit.
  • release.yml could build main HEAD under a tag's name, ran no tests, and would accept a stable tag from any branch.

What

fix:

  • Session restore:
    • Only a refusal from the server signs the user out. Offline, a timeout, a 5xx or a captive portal keeps the session and shows a "can't reach inklet" screen with Try Again.
    • Requests fail fast instead of waiting for connectivity.
  • Tokens: release builds keep tokens only in the Keychain. If a Keychain write fails, the session lives in memory for that launch. A plaintext file left by an older release is migrated into the Keychain and deleted.
  • Location: added com.apple.security.personal-information.location. Weather is now off by default and asks for location only when turned on.
  • AppleScript:
    • Context capture runs in an osascript child process, so the deadline kills it and it never blocks the main thread. NSAppleScript is main-thread-only and cannot be cancelled.
    • Photos exports are collected in one temp folder that gets cleaned up.
  • Attachments: loaded off the main thread, limited to 10 MiB each and 50 per send, using the backend's list of accepted types.
  • Termination: NSSupportsAutomaticTermination and NSSupportsSuddenTermination are set to false, so the menu bar app keeps its hot key and Services when no window is open.
  • API host: one constant, overridable at build time with INKLET_API_BASE_URL for both the app and the widget. The default is still https://dev.iminklet.com.
  • Session edge cases:
    • A 401 on History or Quote/0 signs out like everywhere else.
    • Services still works while signed out.
    • The OAuth callback must be exactly inklet://auth/callback.
    • Settings shows the Accessibility status for capturing highlighted text.

build(release):

  • A resolve-tag job refuses a stable tag that isn't on main. Pre-release tags can still come from branches.
  • The build checks out exactly the commit that job verified.
  • A manual run takes only a tag; the stale version defaults are gone.
  • swift test runs before the signing certificate is imported.

docs: removed the server IP, daemon user, login steps, internal tracker URL and absolute local paths from this public repo. They remain in git history.

Verification

  • swift build is clean. swift test passes 71 tests (49 before). New tests cover session restore, OAuth callback parsing, attachment limits and the API host.
  • ./Scripts/build-app.sh debug builds a bundle that passes codesign --verify --deep --strict, and its entitlements include the location entitlement.
  • ScriptProcess was exercised on its own:
    • A normal result returns in 0.06 s.
    • A 10 s script is killed at a 1 s deadline.
    • A script stopped before launch never starts.
    • 200 KB of output doesn't stall the pipe.

Still needs a Developer ID-signed build

  • The location prompt under the hardened runtime.
  • Automation consent for the osascript child: it should be attributed to inklet Portal, and a Photos export should leave the UI responsive.
  • Keychain-only token storage and migration of the legacy token file.
  • Services from a cold launch, including while signed out.
  • The release workflow itself, which runs on the next tag.

Copilot AI lite review requested due to automatic review settings September 22, 2026 23:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…ime out

- Launch: only the server refusing the session signs out. Offline, a
  timeout, a 5xx, or a captive portal keeps the tokens and shows a
  "can't reach inklet" screen with Try Again; requests no longer wait
  for connectivity, so the splash reaches it in about 20 s.
- Release builds never write tokens to a plaintext file: a Keychain
  failure keeps the session in memory for that launch, and a file left
  by an older release is moved into the Keychain and deleted.
- Location: add the hardened-runtime location entitlement; weather is
  off by default and asks for location only when turned on.
- AppleScript context capture runs in an osascript child, so the
  deadline is real (the process is killed) and nothing blocks the main
  thread. Photos exports live in one temp folder that is emptied at
  launch, on each summon, after a send, and on sign-out.
- Attachments load off the main thread, capped at 10 MiB each and 50
  items per send, with the backend's list of accepted types.
- Opt out of automatic and sudden termination: the menu bar app must
  keep its hot key, Services, and run tracking with no window open.
- One API host in InkletPresentationKit (still dev.iminklet.com),
  overridable at build time with INKLET_API_BASE_URL for the app and
  the widget.
- A 401 on History and Quote/0 signs out like everywhere else; Services
  keeps what was sent while signed out and opens it after sign-in; the
  OAuth callback accepts exactly inklet://auth/callback.
- Settings shows Accessibility status for highlighted-text capture.
  Drop the unsupported public.image Services type and the unused widget
  entitlements file; the README lists the URL scheme actually
  registered.

Tests: 49 -> 71 (session restore, OAuth callback parsing, attachment
limits, API host).
…ags from main

- A first job checks the tag's format and refuses a stable tag whose
  commit is not on origin/main, before any release is created. Pre-release
  tags may still come from a branch.
- The build checks out exactly the commit that job verified. A manual run
  takes only the tag and checks its Release exists, instead of building
  main HEAD under a stale default version.
- swift test runs before the signing certificate is imported.
…docs

The repository is public. The acceptance notes named the server's IP,
its daemon user, how to log in, and an internal tracker; the audit linked
absolute paths on one machine. Say what they meant without them.
@CLCK0622
CLCK0622 force-pushed the fix/release-readiness branch from d77f670 to 318ba21 Compare September 23, 2026 02:22
@CLCK0622
CLCK0622 merged commit 4be882e into main Sep 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants