Class: Aidp::Security::SecretsRegistry
- Inherits:
-
Object
- Object
- Aidp::Security::SecretsRegistry
- Defined in:
- lib/aidp/security/secrets_registry.rb
Overview
Registry for user-declared secrets that should be proxied Secrets are registered by name, with the actual value stored securely and never exposed directly to agents.
Storage: .aidp/security/secrets_registry.json Format: { “SECRET_NAME”: { “env_var”: “ACTUAL_ENV_VAR”, “registered_at”: timestamp } }
The registry only stores metadata - actual secret values come from environment variables at runtime. This ensures secrets are never persisted to disk.
Constant Summary collapse
- REGISTRY_FILENAME =
"secrets_registry.json"
Instance Attribute Summary collapse
-
#project_dir ⇒ Object
readonly
Returns the value of attribute project_dir.
Instance Method Summary collapse
-
#clear_cache! ⇒ Object
Clear the in-memory cache (forces reload on next access).
-
#env_var_for(name) ⇒ String?
Get the environment variable name for a secret.
-
#env_var_registered?(env_var) ⇒ Boolean
Check if an environment variable is registered as a secret.
-
#env_vars_to_strip ⇒ Array<String>
Get list of environment variables that should be stripped from agent environment.
-
#get(name) ⇒ Hash?
Get registration details for a secret (without the actual value).
-
#initialize(project_dir: Dir.pwd) ⇒ SecretsRegistry
constructor
A new instance of SecretsRegistry.
-
#list ⇒ Array<Hash>
List all registered secrets (names and metadata only, never values).
-
#name_for_env_var(env_var) ⇒ String?
Get the secret name for an environment variable.
-
#register(name:, env_var:, description: nil, scopes: []) ⇒ Hash
Register a secret by name.
-
#registered?(name) ⇒ Boolean
Check if a secret is registered.
-
#unregister(name:) ⇒ Boolean
Unregister a secret.
Constructor Details
#initialize(project_dir: Dir.pwd) ⇒ SecretsRegistry
Returns a new instance of SecretsRegistry.
24 25 26 27 28 29 |
# File 'lib/aidp/security/secrets_registry.rb', line 24 def initialize(project_dir: Dir.pwd) @project_dir = project_dir @cache = nil @cache_mtime = nil @mutex = Mutex.new end |
Instance Attribute Details
#project_dir ⇒ Object (readonly)
Returns the value of attribute project_dir.
22 23 24 |
# File 'lib/aidp/security/secrets_registry.rb', line 22 def project_dir @project_dir end |
Instance Method Details
#clear_cache! ⇒ Object
Clear the in-memory cache (forces reload on next access)
169 170 171 172 173 174 |
# File 'lib/aidp/security/secrets_registry.rb', line 169 def clear_cache! @mutex.synchronize do @cache = nil @cache_mtime = nil end end |
#env_var_for(name) ⇒ String?
Get the environment variable name for a secret
116 117 118 119 |
# File 'lib/aidp/security/secrets_registry.rb', line 116 def env_var_for(name) entry = get(name) entry&.dig(:env_var) || entry&.dig("env_var") end |
#env_var_registered?(env_var) ⇒ Boolean
Check if an environment variable is registered as a secret
151 152 153 154 155 156 |
# File 'lib/aidp/security/secrets_registry.rb', line 151 def env_var_registered?(env_var) @mutex.synchronize do registry = load_registry registry.values.any? { |entry| (entry[:env_var] || entry["env_var"]) == env_var } end end |
#env_vars_to_strip ⇒ Array<String>
Get list of environment variables that should be stripped from agent environment
141 142 143 144 145 146 |
# File 'lib/aidp/security/secrets_registry.rb', line 141 def env_vars_to_strip @mutex.synchronize do registry = load_registry registry.values.map { |entry| entry[:env_var] || entry["env_var"] }.compact.uniq end end |
#get(name) ⇒ Hash?
Get registration details for a secret (without the actual value)
103 104 105 106 107 108 109 110 111 |
# File 'lib/aidp/security/secrets_registry.rb', line 103 def get(name) @mutex.synchronize do registry = load_registry entry = registry[name.to_sym] || registry[name.to_s] return nil unless entry entry.merge(name: name) end end |
#list ⇒ Array<Hash>
List all registered secrets (names and metadata only, never values)
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/aidp/security/secrets_registry.rb', line 123 def list @mutex.synchronize do registry = load_registry registry.map do |name, details| { name: name, env_var: details[:env_var] || details["env_var"], description: details[:description] || details["description"], scopes: details[:scopes] || details["scopes"] || [], registered_at: details[:registered_at] || details["registered_at"], has_value: ENV.key?(details[:env_var] || details["env_var"]) } end end end |
#name_for_env_var(env_var) ⇒ String?
Get the secret name for an environment variable
161 162 163 164 165 166 |
# File 'lib/aidp/security/secrets_registry.rb', line 161 def name_for_env_var(env_var) @mutex.synchronize do registry = load_registry registry.find { |_name, entry| (entry[:env_var] || entry["env_var"]) == env_var }&.first end end |
#register(name:, env_var:, description: nil, scopes: []) ⇒ Hash
Register a secret by name
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/aidp/security/secrets_registry.rb', line 37 def register(name:, env_var:, description: nil, scopes: []) @mutex.synchronize do registry = load_registry # Validate the environment variable exists (but don't store the value) unless ENV.key?(env_var) Aidp.log_warn("security.registry", "env_var_not_found", name: name, env_var: env_var) end registration = { env_var: env_var, description: description, scopes: scopes, registered_at: Time.now.iso8601, id: SecureRandom.hex(8) } registry[name] = registration save_registry(registry) Aidp.log_info("security.registry", "secret_registered", name: name, env_var: env_var, scopes: scopes) registration.merge(name: name) end end |
#registered?(name) ⇒ Boolean
Check if a secret is registered
93 94 95 96 97 98 |
# File 'lib/aidp/security/secrets_registry.rb', line 93 def registered?(name) @mutex.synchronize do registry = load_registry registry.key?(name.to_sym) || registry.key?(name.to_s) end end |
#unregister(name:) ⇒ Boolean
Unregister a secret
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/aidp/security/secrets_registry.rb', line 71 def unregister(name:) @mutex.synchronize do registry = load_registry key = registry.key?(name.to_sym) ? name.to_sym : name.to_s unless registry.key?(key) Aidp.log_warn("security.registry", "secret_not_found_for_unregister", name: name) return false end registry.delete(key) save_registry(registry) Aidp.log_info("security.registry", "secret_unregistered", name: name) true end end |