Skip to content

T-6: GitHubCredentialStore — Keychain account-keyed #220

@kirich1409

Description

@kirich1409

Description

Implement public struct GitHubCredentialStore: Sendable in GitHubIntegrationClient/GitHubCredentialStore.swift:

public func save(token: String, for login: String) throws(KeychainError)
public func load(for login: String) throws(KeychainError) -> String?
public func delete(for login: String) throws(KeychainError)
public func listAccounts() throws(KeychainError) -> [String]
public func currentAccount() -> String?
public func setCurrentAccount(_ login: String?)

Keychain attributes:

  • service = "com.relay.github"
  • account = <login> (GitHub username without @)
  • kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
  • Not synchronizable (no iCloud for dev tokens)
public enum KeychainError: Error, Equatable {
    case osStatus(OSStatus)
    case dataCorrupted
}

Current account pointer — in UserDefaults.standard under key com.relay.github.currentAccount. MVP is single active account; listAccounts() + setCurrentAccount prepare API for future multi-account UI.

Spec reference

See swarm-report/github-integration-decomposition.md#t-6.

Relationships

Acceptance criteria

  • save(token: "X", for: "alice")load(for: "alice") returns "X"
  • First-run resilience: load(for: "alice") when nothing was ever saved → returns nil, does NOT throw
  • save + delete(for: "alice") + loadnil
  • listAccounts() returns all previously saved logins
  • currentAccount() persists across process restarts (test with isolated UserDefaults suite)
  • Tests use isolated service name com.relay.github.test.<UUID>, cleanup in tearDown
  • swiftlint lint --strict passes

Complexity

S

Suggested agent

developer-workflow:swift-engineer

Module / Layer

GitHubIntegrationClient / Storage

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions