vaultak 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/vaultak.rb +226 -0
  3. metadata +45 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a3cd3eb63bb56e4ceb3ff51ec94fdc8f338b4813f2a962155f64fae6bf971cb4
4
+ data.tar.gz: 2a3958bc73a515d5f5ced9bd0bc43e5f67d84b62cdf4ec7bcb4d2453ffdedcfe
5
+ SHA512:
6
+ metadata.gz: 26366a685be751a9a4cbda61e98b7d7d614053a4a214f7f95292f6d35e948370bacc1d97f7084feb935ec133bcb653cda5ce22736b9c2659f9ad96963ec4c9e8
7
+ data.tar.gz: 2e3ee2cf02a2b24c13b70be9d9060865575476a1ecb7a0a39e72182ee65e8868d794c2480e8ee8083580df422ee0ce36d936d40b7f95b357ec598851cb284fd5
data/lib/vaultak.rb ADDED
@@ -0,0 +1,226 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'uri'
4
+ require 'fileutils'
5
+ require 'digest'
6
+ require 'time'
7
+
8
+ module Vaultak
9
+ VERSION = "0.6.1"
10
+
11
+ class Client
12
+ def initialize(options = {})
13
+ @api_key = options[:api_key] || ENV['VAULTAK_API_KEY'] || ''
14
+ @agent_id = options[:agent_id] || 'default'
15
+ @alert_threshold = options[:alert_threshold] || 30
16
+ @pause_threshold = options[:pause_threshold] || 60
17
+ @rollback_threshold = options[:rollback_threshold] || 85
18
+ @blocked_resources = options[:blocked_resources] || []
19
+ @allowed_resources = options[:allowed_resources]
20
+ @max_actions_per_minute = options[:max_actions_per_minute] || 60
21
+ @api_endpoint = options[:api_endpoint] || 'https://vaultak.com'
22
+ @session_id = SecureRandom.uuid rescue "#{Time.now.to_i}-#{rand(9999)}"
23
+ @action_times = []
24
+ @file_snapshots = {}
25
+ @paused = false
26
+ @originals = {}
27
+ end
28
+
29
+ def monitor(agent_id = nil, &block)
30
+ @current_agent_id = agent_id || @agent_id
31
+ @_installed = false
32
+ _install
33
+ @_installed = true
34
+ begin
35
+ yield self if block_given?
36
+ rescue VaultakPauseError, VaultakBlockError
37
+ _uninstall if @_installed
38
+ @_installed = false
39
+ raise
40
+ ensure
41
+ _uninstall if @_installed
42
+ @_installed = false
43
+ end
44
+ end
45
+
46
+ def intercept(action_type, resource, payload = {})
47
+ return 'BLOCK' if @paused
48
+
49
+ # Rate limiting
50
+ now = Time.now.to_f
51
+ @action_times.reject! { |t| now - t > 60 }
52
+ if @action_times.length >= @max_actions_per_minute
53
+ _send_action(action_type, resource, payload, 90, 'BLOCK')
54
+ return 'BLOCK'
55
+ end
56
+
57
+ # Policy checks
58
+ @blocked_resources.each do |pattern|
59
+ if File.fnmatch(pattern, resource) || resource.include?(pattern.gsub('*', ''))
60
+ _send_action(action_type, resource, payload, 95, 'BLOCK')
61
+ return 'BLOCK'
62
+ end
63
+ end
64
+
65
+ if @allowed_resources
66
+ allowed = @allowed_resources.any? { |p| File.fnmatch(p, resource) }
67
+ unless allowed
68
+ _send_action(action_type, resource, payload, 80, 'BLOCK')
69
+ return 'BLOCK'
70
+ end
71
+ end
72
+
73
+ score = _compute_score(action_type, resource)
74
+
75
+ if score >= @rollback_threshold
76
+ _send_action(action_type, resource, payload, score, 'ROLLBACK')
77
+ _execute_rollback
78
+ @paused = true
79
+ raise VaultakPauseError, "Risk score #{score} exceeded rollback threshold. State restored."
80
+ elsif score >= @pause_threshold
81
+ _send_action(action_type, resource, payload, score, 'PAUSE')
82
+ @paused = true
83
+ raise VaultakPauseError, "Risk score #{score} exceeded pause threshold. Awaiting review."
84
+ elsif score >= @alert_threshold
85
+ _send_action(action_type, resource, payload, score, 'ALERT')
86
+ else
87
+ _send_action(action_type, resource, payload, score, 'ALLOW')
88
+ end
89
+
90
+ @action_times << now
91
+ 'ALLOW'
92
+ end
93
+
94
+ def snapshot_file(path)
95
+ if File.exist?(path)
96
+ @file_snapshots[path] = File.binread(path)
97
+ else
98
+ @file_snapshots[path] = nil
99
+ end
100
+ end
101
+
102
+ def approve
103
+ @paused = false
104
+ end
105
+
106
+ def log_action(action_type, resource, payload = {})
107
+ _send_action(action_type, resource, payload, 0, 'ALLOW')
108
+ end
109
+
110
+ private
111
+
112
+ def _install
113
+ client = self
114
+
115
+ # Patch File.write
116
+ @originals[:file_write] = File.method(:write)
117
+ File.define_singleton_method(:write) do |path, *args|
118
+ client.snapshot_file(path.to_s)
119
+ decision = client.intercept('file_write', path.to_s, { method: 'File.write' })
120
+ raise VaultakBlockError, "File write blocked: #{path}" if decision == 'BLOCK'
121
+ client.instance_variable_get(:@originals)[:file_write].call(path, *args)
122
+ end
123
+
124
+ # Patch File.delete
125
+ @originals[:file_delete] = File.method(:delete)
126
+ File.define_singleton_method(:delete) do |*paths|
127
+ paths.each do |path|
128
+ decision = client.intercept('delete', path.to_s, {})
129
+ raise VaultakBlockError, "File delete blocked: #{path}" if decision == 'BLOCK'
130
+ end
131
+ client.instance_variable_get(:@originals)[:file_delete].call(*paths)
132
+ end
133
+
134
+ $stderr.puts "[Vaultak] Ruby agent monitoring active: #{@current_agent_id}"
135
+ end
136
+
137
+ def _uninstall
138
+ return if @originals.empty?
139
+ # Store originals locally before clearing
140
+ orig_write = @originals[:file_write]
141
+ orig_delete = @originals[:file_delete]
142
+ @originals = {}
143
+ # Restore File methods using stored originals
144
+ if orig_write
145
+ File.define_singleton_method(:write) { |*args| orig_write.call(*args) }
146
+ end
147
+ if orig_delete
148
+ File.define_singleton_method(:delete) { |*args| orig_delete.call(*args) }
149
+ end
150
+ end
151
+
152
+ def _compute_score(action_type, resource)
153
+ scores = {
154
+ 'file_write' => 40, 'file_read' => 10, 'delete' => 75,
155
+ 'api_call' => 35, 'execute' => 60, 'database_write' => 50,
156
+ 'database_read' => 15
157
+ }
158
+ score = scores[action_type] || 30
159
+ sensitive = %w[prod production secret .env password key token credential]
160
+ score += 30 if sensitive.any? { |p| resource.downcase.include?(p) }
161
+ [score, 100].min
162
+ end
163
+
164
+ def _execute_rollback
165
+ orig_delete = @originals[:file_delete]
166
+ @file_snapshots.each do |path, snapshot|
167
+ begin
168
+ if snapshot.nil?
169
+ if File.exist?(path)
170
+ if orig_delete
171
+ orig_delete.call(path)
172
+ else
173
+ File.unlink(path)
174
+ end
175
+ end
176
+ else
177
+ File.binwrite(path, snapshot)
178
+ end
179
+ $stderr.puts "[Vaultak] Rolled back: #{path}"
180
+ rescue => e
181
+ $stderr.puts "[Vaultak] Rollback failed for #{path}: #{e.message}"
182
+ end
183
+ end
184
+ @file_snapshots = {}
185
+ end
186
+
187
+ def _send_action(action_type, resource, payload, score, decision)
188
+ data = {
189
+ agent_id: @current_agent_id || @agent_id,
190
+ session_id: @session_id,
191
+ action_type: action_type,
192
+ resource: resource,
193
+ payload: payload,
194
+ risk_score: score / 100.0,
195
+ decision: decision,
196
+ timestamp: Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
197
+ source: 'ruby-sdk'
198
+ }.to_json
199
+
200
+ Thread.new do
201
+ begin
202
+ uri = URI("#{@api_endpoint}/api/actions")
203
+ http = Net::HTTP.new(uri.host, uri.port)
204
+ http.use_ssl = uri.scheme == 'https'
205
+ http.open_timeout = 3
206
+ http.read_timeout = 3
207
+ req = Net::HTTP::Post.new(uri.path)
208
+ req['Content-Type'] = 'application/json'
209
+ req['x-api-key'] = @api_key
210
+ req.body = data
211
+ http.request(req)
212
+ rescue
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ class VaultakPauseError < StandardError; end
219
+ class VaultakBlockError < StandardError; end
220
+
221
+ # Convenience method
222
+ def self.monitor(agent_id, options = {}, &block)
223
+ client = Client.new(options)
224
+ client.monitor(agent_id, &block)
225
+ end
226
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vaultak
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.1
5
+ platform: ruby
6
+ authors:
7
+ - Vaultak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-04-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Monitor every action, enforce policies, and automatically roll back damage
14
+ from AI agents.
15
+ email:
16
+ - support@vaultak.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/vaultak.rb
22
+ homepage: https://vaultak.com
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.7.0
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubygems_version: 3.0.3.1
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: Runtime security for autonomous AI agents
45
+ test_files: []