localvault 1.9.2 → 1.11.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f52091ddaad3306c188919a18acc4cae01aff5d0582e3ca07426b6ab3dede07e
4
- data.tar.gz: 4db7f05666bbb3644b436561e82bcbd51b2b8402618885469ec0534abed60302
3
+ metadata.gz: 026a87452013bc8910082a3b582dcbdea76eb0bfccb0730f419e4f1a244b8d3d
4
+ data.tar.gz: 5365ca55bf39269fe4e722054a1d4e0af4ddbe3cf21de4027dcf09f004df6530
5
5
  SHA512:
6
- metadata.gz: 76a6c6007b74d7287673345f0e11b08cc4dca663381b04e6e23e1917d84c92a45f0f9f809be73d028492617b905a66968b0c98791ab0a2772980b94f0e9bcf58
7
- data.tar.gz: 10751a27f1d8bdce859045e678af61a968a18fe65679dc015a60056502ede9fb4de414439f234ca550da3721f0bd63aaa3e3b93895676b653036befd29d0a348
6
+ metadata.gz: 441807ea68cd2f70b0e746a320f7fd1ece69adb13ff3727b5357a4c43de3a077cb52827d57e60003624ef5b7a04f8200acb71f2ca197d1d5b34ce416cecdd0ce
7
+ data.tar.gz: 300fdd00e8b103ee0926ffcf4f45ce95a39a3a3cc0745c7f392177938dded7096518cfc9e014709304fc25b47305e3c38bc8ce3e01767d386fa220d2f554b186
@@ -0,0 +1,55 @@
1
+ require "thor"
2
+
3
+ module LocalVault
4
+ class CLI
5
+ class IdentityCommand < Thor
6
+ ALIAS_PATTERN = /\A[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}\z/
7
+
8
+ desc "show", "Show your identity — alias, InventList handle, public key"
9
+ # Print the local identity summary: the configured alias, the
10
+ # InventList handle (if logged in), and the sync public key (if any).
11
+ def show
12
+ $stdout.puts "Alias: #{Config.identity_alias || "-"}"
13
+ $stdout.puts "Handle: #{Config.inventlist_handle ? "@#{Config.inventlist_handle}" : "-"}"
14
+ $stdout.puts "Public key: #{Identity.exists? ? Identity.public_key : "- (run: localvault keygen)"}"
15
+ end
16
+ default_task :show
17
+
18
+ desc "set FIELD VALUE", "Set an identity field (currently: alias)"
19
+ long_desc <<~DESC
20
+ Set a local identity field.
21
+
22
+ SET YOUR ALIAS (a local display name, independent of InventList login):
23
+ \x05 localvault identity set alias nauman
24
+
25
+ The alias is stored in ~/.localvault/config.yml and never leaves
26
+ your machine.
27
+ DESC
28
+ # Set an identity field. Only +alias+ is supported today.
29
+ def set(field, value)
30
+ unless field == "alias"
31
+ $stderr.puts "Error: Unknown field '#{field}'. Supported: alias"
32
+ exit 1
33
+ end
34
+ unless value.match?(ALIAS_PATTERN)
35
+ $stderr.puts "Error: Alias must be 1-32 characters: letters, digits, '-' or '_', starting with a letter or digit."
36
+ exit 1
37
+ end
38
+ Config.identity_alias = value
39
+ $stdout.puts "Alias set to '#{value}'."
40
+ end
41
+
42
+ desc "unset FIELD", "Clear an identity field (currently: alias)"
43
+ # Clear an identity field. Only +alias+ is supported today.
44
+ def unset(field)
45
+ unless field == "alias"
46
+ $stderr.puts "Error: Unknown field '#{field}'. Supported: alias"
47
+ exit 1
48
+ end
49
+ previous = Config.identity_alias
50
+ Config.identity_alias = nil
51
+ $stdout.puts(previous ? "Alias '#{previous}' cleared." : "No alias set.")
52
+ end
53
+ end
54
+ end
55
+ end
@@ -468,8 +468,8 @@ module LocalVault
468
468
  $stderr.puts " localvault login YOUR_TOKEN"
469
469
  $stderr.puts
470
470
  $stderr.puts "Get your token at: https://inventlist.com/@YOUR_HANDLE/edit#developer"
471
- $stderr.puts "New to InventList? Sign up free at https://inventlist.com"
472
- $stderr.puts "Docs: https://inventlist.com/sites/localvault/series/localvault"
471
+ $stderr.puts "Or use your own server: localvault config set server URL (free InventList account: https://inventlist.com)"
472
+ $stderr.puts "Docs: https://kuickr.co/localvault/series"
473
473
  false
474
474
  end
475
475
 
@@ -94,8 +94,8 @@ module LocalVault
94
94
  $stderr.puts " localvault login YOUR_TOKEN"
95
95
  $stderr.puts
96
96
  $stderr.puts "Get your token at: https://inventlist.com/@YOUR_HANDLE/edit#developer"
97
- $stderr.puts "New to InventList? Sign up free at https://inventlist.com"
98
- $stderr.puts "Docs: https://inventlist.com/sites/localvault/series/localvault"
97
+ $stderr.puts "Or use your own server: localvault config set server URL (free InventList account: https://inventlist.com)"
98
+ $stderr.puts "Docs: https://kuickr.co/localvault/series"
99
99
  return
100
100
  end
101
101
 
@@ -108,87 +108,48 @@ module LocalVault
108
108
 
109
109
  class_option :vault, aliases: "-v", type: :string, desc: "Vault name"
110
110
 
111
+ # Main help = Thor's auto-generated command list (always complete, so new
112
+ # commands appear without touching this file) + a handwritten guide for
113
+ # the concepts and workflows Thor can't derive from command descriptions.
111
114
  def self.help(shell, subcommand = false)
112
115
  shell.say ""
113
116
  shell.say "LocalVault — encrypted local secrets vault with MCP support for AI agents"
114
117
  shell.say " https://inventlist.com/tools/localvault"
115
118
  shell.say ""
119
+ super
120
+ shell.say ""
116
121
  shell.say "GETTING STARTED"
117
- shell.say " localvault login [TOKEN] Log in to InventList (enables sync + team features)"
118
122
  shell.say " localvault init [NAME] Create a new encrypted vault"
119
123
  shell.say " localvault demo Create a demo vault to explore commands"
124
+ shell.say " localvault login [TOKEN] Log in to a sync server (enables sync + team features)"
125
+ shell.say " localvault config set server URL Use your own sync host (default: inventlist.com)"
120
126
  shell.say ""
121
- shell.say "SECRETS"
127
+ shell.say "SAFE SECRET INPUT (preferred over passing values as arguments)"
122
128
  shell.say " printf '%s' \"$SECRET\" | localvault set KEY --stdin"
123
- shell.say " Store a secret without argv/history exposure"
124
- shell.say " localvault set KEY VALUE Store a secret (compatibility path)"
125
- shell.say " localvault set --group G K V Store a secret inside group G"
126
- shell.say " localvault get KEY Retrieve a secret"
127
- shell.say " localvault show Display all secrets (masked by default)"
128
- shell.say " localvault groups [QUERY] List or search stored groups (names only)"
129
- shell.say " localvault list List secret key names"
130
- shell.say " localvault delete KEY Remove a secret"
131
- shell.say " localvault import FILE Bulk-import from .env / .json / .yml"
132
- shell.say " localvault env Export as shell variable assignments"
133
- shell.say " localvault exec -- CMD Run a command with secrets injected as env vars"
134
- shell.say ""
135
- shell.say " Use with any CLI: localvault exec -- inventlist ships list"
136
- shell.say " localvault exec -- curl -H \"Authorization: Bearer $API_KEY\" ..."
129
+ shell.say " Store a secret without argv/history exposure"
137
130
  shell.say ""
138
- shell.say "VAULT MANAGEMENT"
139
- shell.say " localvault vaults List all vaults"
140
- shell.say " localvault switch [VAULT] Switch default vault"
141
- shell.say " localvault rekey [NAME] Change vault passphrase"
142
- shell.say " localvault unlock Cache passphrase for session"
143
- shell.say " localvault lock [NAME] Clear cached passphrase"
144
- shell.say " localvault reset [NAME] Destroy and reinitialize a vault"
145
- shell.say " localvault rename OLD NEW Rename a secret key"
146
- shell.say " localvault copy KEY --to V Copy a secret to another vault"
131
+ shell.say "PROJECTS (dot-notation groups inside one vault)"
132
+ shell.say " localvault set platepose.API_KEY v Store a key in the 'platepose' group"
133
+ shell.say " Filter any command with -p: show -p platepose, exec -p platepose -- CMD"
134
+ shell.say " Delete a whole group: localvault delete 'platepose.*'"
147
135
  shell.say ""
148
- shell.say "SYNC (requires localvault login)"
149
- shell.say " localvault sync Sync all vaults bidirectionally (smart push/pull with conflict detection)"
150
- shell.say " localvault sync --dry-run Show what would happen without making changes"
151
- shell.say " localvault sync push [NAME] Push one vault to cloud"
152
- shell.say " localvault sync pull [NAME] Pull one vault from cloud"
153
- shell.say " localvault sync status Show sync status for all vaults"
136
+ shell.say "USING SECRETS WITH ANY CLI"
137
+ shell.say " localvault exec -- inventlist ships list"
138
+ shell.say " localvault exec -- curl -H \"Authorization: Bearer $API_KEY\" ..."
154
139
  shell.say ""
155
140
  shell.say "TEAM SHARING (requires localvault login)"
156
- shell.say " localvault dashboard Aggregate view: owned vaults, vaults shared with you, legacy shares"
157
- shell.say " localvault verify @HANDLE Check if a person has a published public key"
158
- shell.say " localvault add @HANDLE Add teammate (use --scope KEY... for partial access)"
159
- shell.say " localvault remove @HANDLE Remove teammate (--scope KEY to strip one key, --rotate to re-key)"
160
- shell.say " localvault team init [VAULT] Convert vault to team vault (required before add)"
161
- shell.say " localvault team list [VAULT] List vault members and their access"
162
- shell.say " localvault team rotate [VAULT] Re-key vault, keep all members"
163
- shell.say " localvault team SUBCOMMAND See `localvault help team` for the full team namespace"
164
- shell.say " (also accepts `team add/remove/verify` aliases for the top-level commands)"
165
- shell.say ""
166
- shell.say "KEYS (X25519 identity for vault sharing)"
167
- shell.say " localvault keys generate Generate X25519 identity keypair"
168
- shell.say " localvault keys publish Publish public key so others can share vaults with you"
169
- shell.say " localvault keys show Display your current public key"
170
- shell.say " localvault keys SUBCOMMAND See `localvault help keys` for the full keys namespace"
141
+ shell.say " Convert with `team init`, then `add @HANDLE` / `remove @HANDLE`."
142
+ shell.say " Use --scope KEY... for partial access; `team rotate` re-keys for all members."
143
+ shell.say " `team add/remove/verify` also work as aliases for the top-level commands."
171
144
  shell.say ""
172
145
  shell.say "AI / MCP"
173
- shell.say " localvault install-mcp Configure MCP server in your AI tool"
174
- shell.say " localvault mcp Start MCP server (stdio)"
175
- shell.say " localvault mcp --check Check setup and active-vault readiness"
176
- shell.say " Agents: list names, then use localvault_build_exec for safe injection"
177
- shell.say " localvault guard install Block secrets in agent commands (Claude Code hooks)"
178
- shell.say " localvault guard status Show guard hook installation state"
146
+ shell.say " Agents: list secret names, then use localvault_build_exec for safe injection."
147
+ shell.say " `guard install` blocks plaintext secrets in agent commands (Claude Code hooks)."
179
148
  shell.say ""
180
149
  shell.say "LEGACY SHARING (pre-v1.2 direct share, still works as fallback)"
181
- shell.say " localvault keygen Generate X25519 keypair (same as `keys generate`)"
182
- shell.say " localvault share [VAULT] Share a vault with a user, team, or crew (one-shot copy)"
183
- shell.say " localvault receive Fetch and import vaults shared with you"
184
- shell.say " localvault revoke SHARE_ID Revoke a direct vault share"
150
+ shell.say " keygen / share / receive / revoke — superseded by `keys` + team vaults."
185
151
  shell.say ""
186
- shell.say "OTHER"
187
- shell.say " localvault login --status Show current login status"
188
- shell.say " localvault logout Log out"
189
- shell.say " localvault version Print version"
190
- shell.say " localvault doctor Check install and PATH readiness"
191
- shell.say " localvault help [COMMAND] Full help for any command"
152
+ shell.say "Full help for any command: localvault help COMMAND"
192
153
  shell.say ""
193
154
  end
194
155
 
@@ -768,8 +729,10 @@ module LocalVault
768
729
  require_relative "cli/team"
769
730
  require_relative "cli/sync"
770
731
  require_relative "cli/guard"
732
+ require_relative "cli/identity_cmd"
771
733
 
772
734
  register(Guard, "guard", "guard SUBCOMMAND", "Block plaintext secrets in agent tool traffic (Claude Code hooks)")
735
+ register(IdentityCommand, "identity", "identity SUBCOMMAND", "Show or set your local identity (alias, handle, public key)")
773
736
  register(Keys, "keys", "keys SUBCOMMAND", "Manage your X25519 keypair for vault sharing")
774
737
  register(Team, "team", "team SUBCOMMAND", "Manage vault team access")
775
738
  register(Sync, "sync", "sync SUBCOMMAND", "Sync vaults to InventList cloud")
@@ -798,13 +761,16 @@ module LocalVault
798
761
  $stdout.puts "Public key: #{Identity.public_key}"
799
762
  end
800
763
 
801
- desc "login [TOKEN]", "Log in to InventList — validate token, auto-keygen, publish public key"
764
+ desc "login [TOKEN]", "Log in to a sync server — validate token, auto-keygen, publish public key"
802
765
  method_option :status, type: :boolean, default: false, desc: "Show current login status"
766
+ method_option :server, type: :string, desc: "Sync server URL (persisted; defaults to https://inventlist.com)"
803
767
  def login(token = nil)
768
+ Config.api_url = options[:server] if options[:server]
769
+
804
770
  if options[:status]
805
771
  handle = Config.inventlist_handle
806
772
  if handle
807
- $stdout.puts "Logged in as @#{handle}"
773
+ $stdout.puts "Logged in as @#{handle} (server: #{Config.api_url})"
808
774
  else
809
775
  $stdout.puts "Not logged in. Run: localvault login TOKEN"
810
776
  end
@@ -814,13 +780,26 @@ module LocalVault
814
780
  unless token
815
781
  $stdout.puts "Usage: localvault login YOUR_TOKEN"
816
782
  $stdout.puts
817
- $stdout.puts "Get your token at: https://inventlist.com/@YOUR_HANDLE/edit#developer"
818
- $stdout.puts "New to InventList? Sign up free at https://inventlist.com"
783
+ $stdout.puts "Local vault encryption works without any account or server."
784
+ $stdout.puts "Sync and team features need a sync server. LocalVault is server-agnostic —"
785
+ $stdout.puts "pick either:"
819
786
  $stdout.puts
820
- $stdout.puts "LocalVault sync and team features require a free InventList account."
821
- $stdout.puts "Local vault encryption works without an account."
787
+ $stdout.puts " 1. Your own host (any server implementing the 4-endpoint protocol):"
788
+ $stdout.puts " localvault config set server https://vaulthost.example"
789
+ $stdout.puts " localvault login YOUR_TOKEN"
790
+ $stdout.puts " (or one-shot: localvault login YOUR_TOKEN --server https://vaulthost.example)"
822
791
  $stdout.puts
823
- $stdout.puts "Docs: https://inventlist.com/sites/localvault/series/localvault"
792
+ $stdout.puts " 2. InventList (free account):"
793
+ $stdout.puts " Sign up at https://inventlist.com, then get your token at"
794
+ $stdout.puts " https://inventlist.com/@YOUR_HANDLE/edit#developer"
795
+ $stdout.puts
796
+ $stdout.puts "Login generates your X25519 keypair and publishes the public key"
797
+ $stdout.puts "automatically. To do it manually:"
798
+ $stdout.puts " localvault keys generate # create keypair in ~/.localvault/keys/"
799
+ $stdout.puts " localvault keys publish # upload public key so others can share with you"
800
+ $stdout.puts " localvault keys show # print your public key"
801
+ $stdout.puts
802
+ $stdout.puts "Docs: https://kuickr.co/localvault/series"
824
803
  return
825
804
  end
826
805
 
@@ -836,15 +815,53 @@ module LocalVault
836
815
 
837
816
  client.publish_public_key(Identity.public_key)
838
817
 
839
- $stdout.puts "Logged in as @#{handle}"
840
- $stdout.puts "Public key published to your InventList profile."
818
+ $stdout.puts "Logged in as @#{handle} (server: #{Config.api_url})"
819
+ $stdout.puts "Public key published to your profile."
841
820
  $stdout.puts
842
- $stdout.puts "Next: localvault sync push # sync your vault to the cloud"
821
+ $stdout.puts "Next: localvault sync push # sync your vault to the server"
843
822
  rescue ApiClient::ApiError => e
844
823
  if e.status == 401
845
- $stdout.puts "Invalid token. Check your token at: https://inventlist.com/@YOUR_HANDLE/edit#developer"
824
+ $stdout.puts "Invalid token for #{Config.api_url}."
825
+ $stdout.puts "InventList tokens: https://inventlist.com/@YOUR_HANDLE/edit#developer"
826
+ else
827
+ $stdout.puts "Error connecting to #{Config.api_url}: #{e.message}"
828
+ end
829
+ end
830
+
831
+ desc "config SUBCOMMAND ...", "Get or set CLI configuration (currently: server)"
832
+ long_desc <<~DESC
833
+ Read or write LocalVault configuration in ~/.localvault/config.yml.
834
+
835
+ LocalVault is server-agnostic — point it at any host implementing the
836
+ sync protocol:
837
+
838
+ \x05 localvault config get server
839
+ \x05 localvault config set server https://vaulthost.example
840
+ \x05 localvault config unset server # back to https://inventlist.com
841
+ DESC
842
+ def config(action = "get", field = nil, value = nil)
843
+ unless field == "server" || (action == "get" && field.nil?)
844
+ return abort_with "Unknown config field '#{field}'. Supported: server"
845
+ end
846
+
847
+ case action
848
+ when "get"
849
+ $stdout.puts "server: #{Config.api_url}"
850
+ when "set"
851
+ return abort_with "Usage: localvault config set server URL" unless value
852
+ unless value.match?(%r{\Ahttps?://\S+\z})
853
+ return abort_with "Server must be an http(s) URL, e.g. https://vaulthost.example"
854
+ end
855
+ Config.api_url = value
856
+ $stdout.puts "server set to #{value}"
857
+ $stdout.puts "Note: tokens are per-server — run `localvault login YOUR_TOKEN` for this host."
858
+ when "unset"
859
+ data = Config.load
860
+ data.delete("api_url")
861
+ Config.save(data)
862
+ $stdout.puts "server reset to #{Config.api_url}"
846
863
  else
847
- $stdout.puts "Error connecting to InventList: #{e.message}"
864
+ abort_with "Usage: localvault config [get|set|unset] server [URL]"
848
865
  end
849
866
  end
850
867
 
@@ -1181,8 +1198,8 @@ module LocalVault
1181
1198
  $stderr.puts " localvault login YOUR_TOKEN"
1182
1199
  $stderr.puts
1183
1200
  $stderr.puts "Get your token at: https://inventlist.com/@YOUR_HANDLE/edit#developer"
1184
- $stderr.puts "New to InventList? Sign up free at https://inventlist.com"
1185
- $stderr.puts "Docs: https://inventlist.com/sites/localvault/series/localvault"
1201
+ $stderr.puts "Or use your own server: localvault config set server URL (free InventList account: https://inventlist.com)"
1202
+ $stderr.puts "Docs: https://kuickr.co/localvault/series"
1186
1203
  return
1187
1204
  end
1188
1205
 
@@ -122,6 +122,27 @@ module LocalVault
122
122
  save(data)
123
123
  end
124
124
 
125
+ # Read the local identity alias.
126
+ #
127
+ # @return [String, nil] the stored alias, or nil
128
+ def self.identity_alias
129
+ load["identity_alias"]
130
+ end
131
+
132
+ # Set the local identity alias. Pass +nil+ to clear it.
133
+ #
134
+ # @param a [String, nil] the alias to store
135
+ # @return [void]
136
+ def self.identity_alias=(a)
137
+ data = load
138
+ if a.nil?
139
+ data.delete("identity_alias")
140
+ else
141
+ data["identity_alias"] = a
142
+ end
143
+ save(data)
144
+ end
145
+
125
146
  # Read the InventList API base URL.
126
147
  #
127
148
  # @return [String] the API URL, defaults to "https://inventlist.com"
@@ -1,3 +1,3 @@
1
1
  module LocalVault
2
- VERSION = "1.9.2"
2
+ VERSION = "1.11.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: localvault
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.2
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nauman Tariq
@@ -110,6 +110,7 @@ files:
110
110
  - lib/localvault/cli.rb
111
111
  - lib/localvault/cli/error_presenter.rb
112
112
  - lib/localvault/cli/guard.rb
113
+ - lib/localvault/cli/identity_cmd.rb
113
114
  - lib/localvault/cli/keys.rb
114
115
  - lib/localvault/cli/sync.rb
115
116
  - lib/localvault/cli/team.rb