Background
We're building a macOS application that acts as a CryptoTokenKit software token. The architecture follows the documented pattern: a container app (a long-running agent process) manages token registration and identity updates via TKTokenDriverConfiguration, and a separate appex extension process handles the actual signing operations for client sessions.
What we're doing
At agent startup, the container app calls [TKTokenDriverConfiguration driverConfigurations] to obtain our token driver, then registers a token instance ID:
NSDictionary<TKTokenDriverClassID, TKTokenDriverConfiguration *> *driverConfigurations = [TKTokenDriverConfiguration driverConfigurations]; TKTokenDriverConfiguration driver = / first value from driverConfigurations */;
[driver addTokenConfigurationForTokenInstanceID:@"setoken"];
When the agent renews a certificate, it pushes updated TKTokenKeychainItem objects to ctkd by setting keychainItems on the TKTokenConfiguration:
TKTokenConfiguration *tokenCfg = driver.tokenConfigurations[@"setoken"]; tokenCfg.keychainItems = updatedItems;
This works correctly during normal operation.
The failure
When ctkd is restarted (e.g., killall ctkd, or the system restarts the daemon), all subsequent calls through the existing TKTokenDriverConfiguration reference silently fail. Specifically:
- [TKTokenDriverConfiguration driverConfigurations] returns the same stale object - it does not establish a new connection to the newly-started ctkd process. There is no error, no exception, and no indication the returned object is invalid.
- driver.tokenConfigurations[@"setoken"] still returns a non-nil value reflecting the pre-restart state - so any nil check intended to detect "token not registered with ctkd" does not fire.
- [driver addTokenConfigurationForTokenInstanceID:@"setoken"] appears to succeed (no error) but the token is not actually registered with the new ctkd instance.
- Setting tokenCfg.keychainItems = updatedItems appears to succeed but the new ctkd instance has no knowledge of the update.
The only reliable recovery we've found is restarting the container app process itself, at which point [TKTokenDriverConfiguration driverConfigurations] returns a fresh object connected to the new ctkd instance.
What we've investigated
- There is no public API on TKTokenDriverConfiguration to invalidate or refresh the internal XPC connection to ctkd
- TKTokenWatcher can observe token insertions/removals, but we found no documented way to use it to detect a ctkd process restart specifically
- The NSXPCConnection invalidation handler pattern is not accessible through the TKTokenDriverConfiguration abstraction
- Moving credential management into the appex extension. Since the appex extension is recreated when the ctkd process restarts, we are able to update keychainItems from the extension. However, this comes with it's own set of problems: the extension is ephemeral and using the keychain APIs directly from the extension is not well documented and does not appear to be a supported pattern.
Questions
- Is there a supported API to detect that ctkd has restarted and that the existing TKTokenDriverConfiguration reference is no longer valid?
- Is there a supported way to obtain a fresh TKTokenDriverConfiguration without restarting the container app?
- Should the container app be re-architected to avoid holding long-lived TKTokenDriverConfiguration references?