Class: Terret::Credentials

Inherits:
Hames::Service
  • Object
show all
Defined in:
lib/terret/credentials.rb

Overview

ctx — plan §6.9. The one place a provider's secret is resolved, and its security point: every value it resolves is fed to the session scrubber (Sessions#register_scrubber), so a resolved credential can never reach the durable log even if a tool echoes it back into a result. That is what makes this more than a lookup table — it closes the loop the config-pattern redactor (docs/exec.md §6) leaves open, catching a secret by its exact bytes rather than by a shape a deployment had to name in advance.

Resolution order is ENV-first by convention (<PROVIDER>_API_KEY), then an optional encrypted file store. ENV ALWAYS wins; the file is consulted only when ENV is silent (unset or empty), and a file present with no master key REFUSES rather than falling back to anything unprotected.

On-disk format (a deployment writes it; a trt credentials set CLI is future work, plan §14 — only the format is promised here): JSON at the configured file: path, mapping each provider name to

Base64.strict_encode64( iv(12 bytes) || auth_tag(16 bytes) || ciphertext )

each entry AES-256-GCM under a 32-byte master key. The master key is ENV TERRET_CREDENTIALS_KEY, itself Base64 of exactly 32 bytes — generate one with openssl rand -base64 32. Both the file and the key are optional: with neither, ENV <PROVIDER>_API_KEY resolves on its own and the file store is inert, which is the shipped default.

Deferred (plan §14): an OS-keychain backend, and the writer CLI above.

Defined Under Namespace

Classes: Error

Constant Summary collapse

IV_LEN =

AES-GCM standard nonce

12
TAG_LEN =

AES-GCM authentication tag

16
KEY_LEN =

AES-256

32
MIN_SCRUB_LENGTH =

Below this an exact-string scrub would match far too much: the empty string inserts the token between every character, and a two-character value paints ordinary prose. A real resolved credential dwarfs this floor, so a value under it is still resolved and returned — it just does not become an active scrub pattern (fail-safe: never corrupt the log to chase a value too short to be a secret worth catching by its bytes).

8
REPLACEMENT =
"[REDACTED]"

Instance Method Summary collapse

Instance Method Details

#reconfigure(_config) ⇒ Object

the file path is read per resolve



68
# File 'lib/terret/credentials.rb', line 68

def reconfigure(_config); end

#resolve(provider) ⇒ Object

The credential for a provider by name, or nil. ENV wins; then the file store when configured. A non-trivial resolved value is registered as an exact-string scrub pattern before it is returned, so anything that later echoes it into the log is caught.



74
75
76
77
78
79
80
# File 'lib/terret/credentials.rb', line 74

def resolve(provider)
  name = provider.to_s
  env = ENV["#{env_name(name)}_API_KEY"]
  value = (env unless env.nil? || env.empty?) || from_file(name)
  remember(value) if value
  value
end

#start(ctx) ⇒ Object



59
60
61
62
63
64
65
66
# File 'lib/terret/credentials.rb', line 59

def start(ctx)
  @secrets = []
  @mutex = Mutex.new
  # ONE scrubber over the growing set rather than a fresh registration per
  # resolve: re-resolving a provider must not stack duplicate scrubbers, and
  # a scrubber reaches every append whether it was registered early or late.
  ctx[:sessions].register_scrubber(method(:scrub))
end