vaultkit 1.0.1 → 1.0.2

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vkit/cli/base_cli.rb +91 -0
  3. data/lib/vkit/cli/commands/grant_revoke_command.rb +39 -0
  4. data/lib/vkit/cli/commands/init_command.rb +82 -0
  5. data/lib/vkit/cli/commands/policy_pack_add_command.rb +38 -0
  6. data/lib/vkit/cli/commands/policy_pack_info_command.rb +47 -0
  7. data/lib/vkit/cli/commands/policy_pack_list_command.rb +47 -0
  8. data/lib/vkit/cli/commands/policy_pack_remove_command.rb +36 -0
  9. data/lib/vkit/cli/commands/policy_pack_upgrade_command.rb +81 -0
  10. data/lib/vkit/cli/commands/policy_revoke_command.rb +40 -0
  11. data/lib/vkit/cli/policy_pack/manager.rb +416 -0
  12. data/lib/vkit/policy/bundle_compiler.rb +18 -0
  13. data/lib/vkit/policy/packs/ai_safety/metadata.yaml +10 -0
  14. data/lib/vkit/policy/packs/ai_safety/policies/01_deny_agent_prod_without_clearance.yaml +14 -0
  15. data/lib/vkit/policy/packs/ai_safety/policies/02_require_approval_for_sensitive_in_prod.yaml +16 -0
  16. data/lib/vkit/policy/packs/ai_safety/policies/03_mask_sensitive_by_default_for_agents.yaml +15 -0
  17. data/lib/vkit/policy/packs/financial_compliance/metadata.yaml +10 -0
  18. data/lib/vkit/policy/packs/financial_compliance/policies/01_require_approval_for_financial_prod.yaml +16 -0
  19. data/lib/vkit/policy/packs/financial_compliance/policies/02_mask_payment_tokens.yaml +14 -0
  20. data/lib/vkit/policy/packs/financial_compliance/policies/03_deny_non_admin_access_pci_in_prod.yaml +15 -0
  21. data/lib/vkit/policy/packs/financial_compliance/policies/04_short_ttl_for_financial_grants.yaml +14 -0
  22. data/lib/vkit/policy/packs/starter/metadata.yaml +9 -0
  23. data/lib/vkit/policy/packs/starter/policies/01_deny_sensitive_without_clearance.yaml +15 -0
  24. data/lib/vkit/policy/packs/starter/policies/02_mask_pii_by_default.yaml +13 -0
  25. data/lib/vkit/policy/packs/starter/policies/03_require_approval_high_sensitivity.yaml +17 -0
  26. data/lib/vkit/policy/packs/starter/policies/04_block_cross_region.yaml +14 -0
  27. data/lib/vkit/policy/packs/starter/policies/05_limit_production_access.yaml +16 -0
  28. data/lib/vkit/policy/packs/starter/policies/06_default_ttl.yaml +10 -0
  29. data/lib/vkit/policy/schema/policy_bundle.schema.json +16 -1
  30. data/lib/vkit/version.rb +1 -1
  31. metadata +27 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4b12d7696d6e2ec91bdf7843bd8648a1c9f0fdce6c59a6c4cbc902b3e4739c8
4
- data.tar.gz: 7ae893269eb4075c797294585af9f28ad5da121f892b11e7d2e12ea8f625e956
3
+ metadata.gz: 5a8fc789ba6238e633e0638eb06e2bae78b32343dbbbee1c7467bae701a2b38d
4
+ data.tar.gz: a43f1ae331cf39178d22a9f44ca423f3c45dfea81d2265905219432a8f9eca01
5
5
  SHA512:
6
- metadata.gz: 48fc209dc487ee6649012b5d59da82946b37339cf4d3e1875d34c98146e07c79b93147ea510b98674736b6baf81c422a317f699c287103d0fe354e9191aadfb0
7
- data.tar.gz: 62970b31c4cc95ab323e8f2868e41c4214905e680b427a9fe2475621d3e705b0d690c1ad44c113abd5d0b4186398322485b617cca332376c66abdeaacf0d4f6d
6
+ metadata.gz: 109100e282f4c59e53017492db1a440f6c86d3b6045dce06deb32a2a75c57bcfe908acd28fc94f6e8666382a953c54d4e82d528b59792bc72338d78c90ee3a3c
7
+ data.tar.gz: 1945666c96230884896912a8a6d970eabe743fb412894c17251573dd107aa64cb20ea1997e231bb874eb06b4154821b73adec83827cd1cd899138a08ec8c22e3
@@ -36,6 +36,18 @@ module Vkit
36
36
  Commands::ResetCommand.new.call
37
37
  end
38
38
 
39
+ desc "init", "Initialize a new VaultKit project"
40
+ option :dir, type: :string, default: ".", desc: "Target directory"
41
+ option :with, type: :string, desc: "Comma-separated policy packs to install (e.g. starter,ai_safety)"
42
+ def init
43
+ packs = options[:with]&.split(",")&.map(&:strip)
44
+
45
+ Commands::InitCommand.new.call(
46
+ dir: options[:dir],
47
+ packs: packs
48
+ )
49
+ end
50
+
39
51
  # REQUEST
40
52
  desc "request", "Send an inline JSON AQL request (use --aql or pipe via STDIN)"
41
53
  option :aql, type: :string, desc: "AQL JSON payload (inline)"
@@ -101,6 +113,18 @@ module Vkit
101
113
  )
102
114
  end
103
115
 
116
+ desc "grant:revoke", "Revoke a grant"
117
+ option :grant, type: :string, required: true
118
+ option :reason, type: :string
119
+ option :force, type: :boolean, default: false
120
+ define_method("grant:revoke") do
121
+ Commands::GrantRevokeCommand.new.call(
122
+ grant_ref: options[:grant],
123
+ reason: options[:reason],
124
+ force: options[:force]
125
+ )
126
+ end
127
+
104
128
  # DATASOURCE
105
129
  desc "datasource SUBCOMMAND ...ARGS", "Manage datasources (admin only)"
106
130
  subcommand "datasource", Class.new(Thor) {
@@ -186,6 +210,73 @@ module Vkit
186
210
  activate: options[:activate]
187
211
  )
188
212
  end
213
+
214
+ desc "revoke", "Revoke a policy bundle version"
215
+ option :bundle_version, type: :string, required: true
216
+ option :reason, type: :string
217
+ option :force, type: :boolean, default: false
218
+
219
+ def revoke
220
+ Commands::PolicyRevokeCommand.new.call(
221
+ bundle_version: options[:bundle_version],
222
+ reason: options[:reason],
223
+ force: options[:force]
224
+ )
225
+ end
226
+
227
+ desc "pack SUBCOMMAND ...ARGS", "Manage policy packs"
228
+ subcommand "pack", Class.new(Thor) {
229
+
230
+ desc "list", "List policy packs"
231
+ option :dir, type: :string, default: "."
232
+ def list
233
+ Commands::PolicyPackListCommand.new.call(
234
+ dir: options[:dir]
235
+ )
236
+ end
237
+
238
+ desc "add NAME", "Install a policy pack"
239
+ option :dir, type: :string, default: "."
240
+ option :force, type: :boolean, default: false
241
+ def add(name)
242
+ Commands::PolicyPackAddCommand.new.call(
243
+ pack_name: name,
244
+ dir: options[:dir],
245
+ force: options[:force]
246
+ )
247
+ end
248
+
249
+ desc "remove NAME", "Remove an installed policy pack"
250
+ option :dir, type: :string, default: "."
251
+ option :force, type: :boolean, default: false
252
+ def remove(name)
253
+ Commands::PolicyPackRemoveCommand.new.call(
254
+ pack_name: name,
255
+ dir: options[:dir],
256
+ force: options[:force]
257
+ )
258
+ end
259
+
260
+ desc "info NAME", "Show information about a policy pack"
261
+ option :dir, type: :string, default: "."
262
+ def info(name)
263
+ Commands::PolicyPackInfoCommand.new.call(
264
+ pack_name: name,
265
+ dir: options[:dir]
266
+ )
267
+ end
268
+
269
+ desc "upgrade [NAME]", "Upgrade installed policy packs (all if NAME omitted)"
270
+ option :dir, type: :string, default: "."
271
+ option :force, type: :boolean, default: false
272
+ def upgrade(name = nil)
273
+ Commands::PolicyPackUpgradeCommand.new.call(
274
+ pack_name: name,
275
+ dir: options[:dir],
276
+ force: options[:force]
277
+ )
278
+ end
279
+ }
189
280
  }
190
281
 
191
282
  desc "agents SUBCOMMAND ...ARGS", "Manage agents and automation identities"
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vkit
4
+ module CLI
5
+ module Commands
6
+ class GrantRevokeCommand < BaseCommand
7
+ def call(grant_ref:, reason:, force:)
8
+ with_auth do
9
+ user = credential_store.user
10
+ org = user["organization_slug"]
11
+
12
+ raise "Not authenticated. Please login." if org.nil? || org.empty?
13
+
14
+ unless force
15
+ puts "⚠️ You are about to revoke grant: #{grant_ref}"
16
+ puts " Organization: #{org}"
17
+ puts " Reason: #{reason || 'Grant revoked via CLI'}"
18
+ print "Continue? (y/N): "
19
+ answer = STDIN.gets.strip
20
+ return puts("Aborted.") unless answer.downcase == "y"
21
+ end
22
+
23
+ response =
24
+ authenticated_client.post(
25
+ "/api/v1/orgs/#{org}/grants/#{grant_ref}/revoke",
26
+ body: {
27
+ reason: reason
28
+ }
29
+ )
30
+
31
+ puts "✅ Grant revoked"
32
+ puts " Grant: #{grant_ref}"
33
+ puts " Revoked: #{response["revoked_at"]}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require_relative "base_command"
5
+ require_relative "../policy_pack/manager"
6
+
7
+ module Vkit
8
+ module CLI
9
+ module Commands
10
+ class InitCommand < BaseCommand
11
+ def call(dir:, packs:)
12
+ dir = File.expand_path(dir)
13
+ FileUtils.mkdir_p(dir)
14
+
15
+ create_structure(dir)
16
+
17
+ manager = Vkit::CLI::PolicyPack::Manager.new(
18
+ project_root: dir
19
+ )
20
+
21
+ total_policies = 0
22
+ installed = []
23
+
24
+ # Default secure behavior
25
+ if packs.nil? || packs.empty?
26
+ puts "\nℹ️ No packs specified. Installing 'starter' for secure defaults."
27
+ packs = ["starter"]
28
+ end
29
+
30
+ packs.each do |pack_name|
31
+ count = manager.install_with_deps!(pack_name)
32
+ total_policies += count
33
+ installed << pack_name
34
+
35
+ metadata = manager.pack_metadata(pack_name)
36
+ puts "✓ Installed #{pack_name} pack (v#{metadata['version']}) - #{count} policies"
37
+ end
38
+
39
+ puts "\n✅ VaultKit project initialized"
40
+ puts " Location: #{dir}"
41
+ puts " Packs: #{installed.join(', ')}"
42
+ puts " Policies: #{total_policies} total"
43
+
44
+ puts "\nNext steps:"
45
+ puts " 1. Review policies in config/policies/"
46
+ puts " 2. Run: vkit scan <datasource> --apply"
47
+ puts " 3. Run: vkit policy bundle"
48
+ puts " 4. Run: vkit policy deploy"
49
+
50
+ rescue StandardError => e
51
+ puts "❌ Error: #{e.message}"
52
+ exit 1
53
+ end
54
+
55
+ private
56
+
57
+ def create_structure(dir)
58
+ FileUtils.mkdir_p(File.join(dir, "config", "policies"))
59
+ FileUtils.mkdir_p(File.join(dir, "datasets"))
60
+ FileUtils.mkdir_p(File.join(dir, "dist"))
61
+ FileUtils.mkdir_p(File.join(dir, ".vkit"))
62
+
63
+ registry_path = File.join(dir, "datasets", "registry.yaml")
64
+ unless File.exist?(registry_path)
65
+ File.write(
66
+ registry_path,
67
+ "# Dataset registry\n# Populate with: vkit scan <datasource> --apply\n"
68
+ )
69
+ end
70
+
71
+ gitignore_path = File.join(dir, ".gitignore")
72
+ unless File.exist?(gitignore_path)
73
+ File.write(
74
+ gitignore_path,
75
+ "dist/\n.vkit/\n"
76
+ )
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_command"
4
+
5
+ module Vkit
6
+ module CLI
7
+ module Commands
8
+ class PolicyPackAddCommand < BaseCommand
9
+ def call(pack_name:, dir:, force: false)
10
+ ensure_project!(dir)
11
+
12
+ manager = Vkit::CLI::PolicyPack::Manager.new(
13
+ project_root: dir
14
+ )
15
+
16
+ count = manager.install!(pack_name, force: force)
17
+ meta = manager.pack_metadata(pack_name)
18
+
19
+ puts "✅ Installed #{pack_name}"
20
+ puts " Version: v#{meta['version']}"
21
+ puts " Policies: #{count} added"
22
+ puts "\nRun 'vkit policy bundle' to compile."
23
+ rescue StandardError => e
24
+ puts "❌ Error: #{e.message}"
25
+ exit 1
26
+ end
27
+
28
+ private
29
+
30
+ def ensure_project!(dir)
31
+ dir = File.expand_path(dir)
32
+ raise "Directory does not exist: #{dir}" unless Dir.exist?(dir)
33
+ raise "Not a VaultKit project (missing .vkit/)" unless Dir.exist?(File.join(dir, ".vkit"))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_command"
4
+
5
+ module Vkit
6
+ module CLI
7
+ module Commands
8
+ class PolicyPackInfoCommand < BaseCommand
9
+ def call(pack_name:, dir:)
10
+ manager = Vkit::CLI::PolicyPack::Manager.new(
11
+ project_root: dir
12
+ )
13
+
14
+ meta = manager.pack_metadata(pack_name)
15
+
16
+ puts "\nPolicy Pack: #{meta['name']}"
17
+ puts "Version: v#{meta['version']}"
18
+ puts "Layer: #{meta['layer'] || 'n/a'}"
19
+
20
+ deps = Array(meta["dependencies"])
21
+ puts "Dependencies: #{deps.empty? ? 'none' : deps.join(', ')}"
22
+
23
+ band = meta["priority_band"]
24
+ if band
25
+ puts "Priority Band: #{band['min']} – #{band['max']}"
26
+ end
27
+
28
+ puts "\nPolicies Included:"
29
+ policies = manager.send(:read_pack!, pack_name).last
30
+ policies.each do |p|
31
+ puts " - #{p['id']}"
32
+ end
33
+
34
+ if manager.installed?(pack_name)
35
+ entry = manager.installed_packs[pack_name]
36
+ puts "\nInstalled: yes (v#{entry['version']})"
37
+ else
38
+ puts "\nInstalled: no"
39
+ end
40
+ rescue StandardError => e
41
+ puts "❌ Error: #{e.message}"
42
+ exit 1
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_command"
4
+
5
+ module Vkit
6
+ module CLI
7
+ module Commands
8
+ class PolicyPackListCommand < BaseCommand
9
+ def call(dir:)
10
+ manager = Vkit::CLI::PolicyPack::Manager.new(
11
+ project_root: dir
12
+ )
13
+
14
+ packs = manager.list_status
15
+
16
+ if packs.empty?
17
+ puts "No policy packs available."
18
+ return
19
+ end
20
+
21
+ puts "Available policy packs:\n\n"
22
+
23
+ packs.each do |p|
24
+ name = p["name"]
25
+ shipped = p["shipped_version"]
26
+ installed = p["installed"]
27
+ installed_version = p["installed_version"]
28
+ drift = p["drift"]
29
+
30
+ if installed
31
+ if drift
32
+ puts "⚠ #{name} (installed v#{installed_version}, available v#{shipped})"
33
+ else
34
+ puts "✓ #{name} v#{installed_version}"
35
+ end
36
+ else
37
+ puts " #{name} (not installed)"
38
+ end
39
+ end
40
+ rescue StandardError => e
41
+ puts "❌ Error: #{e.message}"
42
+ exit 1
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_command"
4
+
5
+ module Vkit
6
+ module CLI
7
+ module Commands
8
+ class PolicyPackRemoveCommand < BaseCommand
9
+ def call(pack_name:, dir:, force: false)
10
+ ensure_project!(dir)
11
+
12
+ manager = Vkit::CLI::PolicyPack::Manager.new(
13
+ project_root: dir
14
+ )
15
+
16
+ removed = manager.remove!(pack_name, force: force)
17
+
18
+ puts "✅ Removed #{pack_name}"
19
+ puts " Files deleted: #{removed}"
20
+ puts "\nRun 'vkit policy bundle' to recompile."
21
+ rescue StandardError => e
22
+ puts "❌ Error: #{e.message}"
23
+ exit 1
24
+ end
25
+
26
+ private
27
+
28
+ def ensure_project!(dir)
29
+ dir = File.expand_path(dir)
30
+ raise "Directory does not exist: #{dir}" unless Dir.exist?(dir)
31
+ raise "Not a VaultKit project (missing .vkit/)" unless Dir.exist?(File.join(dir, ".vkit"))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_command"
4
+
5
+ module Vkit
6
+ module CLI
7
+ module Commands
8
+ class PolicyPackUpgradeCommand < BaseCommand
9
+ def call(pack_name:, dir:, force: false)
10
+ ensure_project!(dir)
11
+
12
+ manager = Vkit::CLI::PolicyPack::Manager.new(
13
+ project_root: dir
14
+ )
15
+
16
+ results =
17
+ if pack_name
18
+ [upgrade_single(manager, pack_name, force)]
19
+ else
20
+ manager.upgrade_all!(force: force)
21
+ end
22
+
23
+ print_summary(results)
24
+
25
+ puts "\nRun 'vkit policy bundle' to recompile."
26
+ rescue StandardError => e
27
+ puts "❌ Error: #{e.message}"
28
+ exit 1
29
+ end
30
+
31
+ private
32
+
33
+ def upgrade_single(manager, pack, force)
34
+ result = manager.upgrade!(pack, force: force)
35
+
36
+ if result == :up_to_date
37
+ { name: pack, status: :up_to_date }
38
+ else
39
+ {
40
+ name: pack,
41
+ status: :upgraded,
42
+ old: result[:old_version],
43
+ new: result[:new_version],
44
+ policies: result[:policies]
45
+ }
46
+ end
47
+ rescue => e
48
+ { name: pack, status: :failed, error: e.message }
49
+ end
50
+
51
+ def print_summary(results)
52
+ return if results.empty?
53
+
54
+ puts "\nPolicy Pack Upgrade Summary\n"
55
+
56
+ results.each do |r|
57
+ case r[:status]
58
+ when :up_to_date
59
+ puts "✓ #{r[:name]} is already up to date"
60
+
61
+ when :upgraded
62
+ puts "✅ #{r[:name]}"
63
+ puts " From: v#{r[:old]}"
64
+ puts " To: v#{r[:new]}"
65
+ puts " Policies updated: #{r[:policies]}"
66
+
67
+ when :failed
68
+ puts "❌ #{r[:name]}: #{r[:error]}"
69
+ end
70
+ end
71
+ end
72
+
73
+ def ensure_project!(dir)
74
+ dir = File.expand_path(dir)
75
+ raise "Directory does not exist: #{dir}" unless Dir.exist?(dir)
76
+ raise "Not a VaultKit project (missing .vkit/)" unless Dir.exist?(File.join(dir, ".vkit"))
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vkit
4
+ module CLI
5
+ module Commands
6
+ class PolicyRevokeCommand < BaseCommand
7
+ def call(bundle_version:, reason:, force:)
8
+ with_auth do
9
+ user = credential_store.user
10
+ org = user["organization_slug"]
11
+
12
+ raise "Not authenticated. Please login." if org.nil? || org.empty?
13
+
14
+ unless force
15
+ puts "⚠️ You are about to revoke policy bundle version: #{bundle_version}"
16
+ puts " Organization: #{org}"
17
+ puts " Reason: #{reason || 'Bundle revoked via CLI'}"
18
+ print "Continue? (y/N): "
19
+ answer = STDIN.gets.strip
20
+ return puts("Aborted.") unless answer.downcase == "y"
21
+ end
22
+
23
+ response =
24
+ authenticated_client.post(
25
+ "/api/v1/orgs/#{org}/policy_bundles/#{bundle_version}/revoke",
26
+ body: {
27
+ reason: reason
28
+ }
29
+ )
30
+
31
+ puts "✅ Policy bundle revoked"
32
+ puts " Version: #{response["bundle_version"]}"
33
+ puts " Checksum: #{response["checksum"]}"
34
+ puts " Revoked: #{response["revoked_at"]}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end