Module: Mysigner::LocalCredentials

Defined in:
lib/mysigner/local_credentials.rb

Overview

Local-only credential store for the CLI’s local-only mode (Epic mysigner-22). Credentials never leave the user’s machine.

On macOS, secrets live in the system Keychain under a dedicated service (separate from Config’s encryption-key service). On other platforms, the secret is AES-256-GCM-encrypted under the same per-machine key Config already manages, and stored as a 0600 file in ~/.mysigner/credentials/.

‘list(kind:)` reads a small index file. The index is a convenience for enumeration only; the Keychain entries (or the per-credential encrypted files) are the authoritative source. If the index drifts, callers should treat fetch as the source of truth.

Defined Under Namespace

Classes: LocalCredentialsError

Constant Summary collapse

KINDS =
%i[asc google_play apple_ads android_keystore].freeze
KEYCHAIN_SERVICE =
'com.mysigner.cli.credentials'
CREDENTIALS_DIR =
File.expand_path('~/.mysigner/credentials').freeze
INDEX_DIR =
File.join(CREDENTIALS_DIR, '.index').freeze

Class Method Summary collapse

Class Method Details

.delete(kind:, id:) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/mysigner/local_credentials.rb', line 56

def delete(kind:, id:)
  validate_kind_and_id!(kind: kind, id: id)

  if macos?
    delete_from_keychain(kind, id)
  else
    delete_from_file(kind, id)
  end

  update_index(kind, id, :remove)
  true
end

.exists?(kind:, id:) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/mysigner/local_credentials.rb', line 85

def exists?(kind:, id:)
  !fetch(kind: kind, id: id).nil?
end

.fetch(kind:, id:) ⇒ Object



46
47
48
49
50
51
52
53
54
# File 'lib/mysigner/local_credentials.rb', line 46

def fetch(kind:, id:)
  validate_kind_and_id!(kind: kind, id: id)

  if macos?
    fetch_from_keychain(kind, id)
  else
    fetch_from_file(kind, id)
  end
end

.list(kind:) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/mysigner/local_credentials.rb', line 69

def list(kind:)
  validate_kind!(kind)

  index_path = index_file_for(kind)
  return [] unless File.exist?(index_path)

  data = JSON.parse(File.read(index_path))
  return [] unless data.is_a?(Array)

  data.map(&:to_s)
rescue JSON::ParserError
  # A corrupt index is non-fatal — the per-credential entries are
  # authoritative. Return empty so the caller can re-store and rebuild.
  []
end

.store(kind:, id:, secret:) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/mysigner/local_credentials.rb', line 33

def store(kind:, id:, secret:)
  validate!(kind: kind, id: id, secret: secret)

  if macos?
    store_in_keychain(kind, id, secret)
  else
    store_in_file(kind, id, secret)
  end

  update_index(kind, id, :add)
  true
end