nano-bots 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1f9a6021523f5e49b4349b2d3010c6c147aa81f00e7e0cb21a33e75e59b6f67
4
- data.tar.gz: 0745255f21ed6786ceec74ac0b7dad6e431a36c05ecc69d1c8601b1c546694de
3
+ metadata.gz: 4632306b0fe1fc9af4ee96c7624f163d80c70eb259c80f3efad158e95e76414a
4
+ data.tar.gz: a2156c47648e3b8b118a4698d23031ca4bd4bf139db6f5f19fa5561f1591cf7b
5
5
  SHA512:
6
- metadata.gz: 208b13813f692bbb3bb7121e553a95df7a88785964e6ccaaef1e4b1a72086a989c46a8c17930e2f6a9ea3cf18a5906b90f37b9d021e2d66ba1d06c072a122587
7
- data.tar.gz: 3aaae5b79c7596b4cd20b1d15687986538573bfd944343129441f5033058d3ce6ff23664c29253a5768a8dcca6ea7385c90acdc16bfb2c72ed0eabad93fb3160
6
+ metadata.gz: d8271268413e87793b147385c999edddbb59dc70203dfca243adb7836ee680dc430a57dc625d038394b6b86e3bb0a9a4e45f12291f9e1ef9574da8ee69ed93e5
7
+ data.tar.gz: 1d6d1118162ed3dbc3438205aa7b4e858499d154e7cc6bdba963a53ddeeff7c071398e9eebc1f3d273c7aa128c39fbcb452a8f6f9bfff8d41b2dac81fb04d9ce
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nano-bots (0.0.9)
4
+ nano-bots (0.1.0)
5
5
  babosa (~> 2.0)
6
6
  dotenv (~> 2.8, >= 2.8.1)
7
7
  faraday (~> 2.7, >= 2.7.5)
8
8
  pry (~> 0.14.2)
9
9
  rainbow (~> 3.1, >= 3.1.1)
10
+ rbnacl (~> 7.1, >= 7.1.1)
10
11
  ruby-openai (~> 4.0)
11
12
  sweet-moon (~> 0.0.7)
12
13
 
@@ -35,6 +36,8 @@ GEM
35
36
  coderay (~> 1.1)
36
37
  method_source (~> 1.0)
37
38
  rainbow (3.1.1)
39
+ rbnacl (7.1.1)
40
+ ffi
38
41
  regexp_parser (2.8.0)
39
42
  rexml (3.2.5)
40
43
  rspec (3.12.0)
@@ -50,7 +53,7 @@ GEM
50
53
  diff-lcs (>= 1.2.0, < 2.0)
51
54
  rspec-support (~> 3.12.0)
52
55
  rspec-support (3.12.0)
53
- rubocop (1.51.0)
56
+ rubocop (1.52.0)
54
57
  json (~> 2.3)
55
58
  parallel (~> 1.10)
56
59
  parser (>= 3.2.0.0)
@@ -60,7 +63,7 @@ GEM
60
63
  rubocop-ast (>= 1.28.0, < 2.0)
61
64
  ruby-progressbar (~> 1.7)
62
65
  unicode-display_width (>= 2.4.0, < 3.0)
63
- rubocop-ast (1.28.1)
66
+ rubocop-ast (1.29.0)
64
67
  parser (>= 3.2.1.0)
65
68
  rubocop-capybara (2.18.0)
66
69
  rubocop (~> 1.41)
data/README.md CHANGED
@@ -13,6 +13,11 @@ https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b
13
13
  - [Command Line](#command-line)
14
14
  - [Library](#library)
15
15
  - [Cartridges](#cartridges)
16
+ - [Marketplace](#marketplace)
17
+ - [Security and Privacy](#security-and-privacy)
18
+ - [Cryptography](#cryptography)
19
+ - [End-user IDs](#end-user-ids)
20
+ - [Decrypting](#decrypting)
16
21
  - [Providers](#providers)
17
22
  - [Debugging](#debugging)
18
23
  - [Development](#development)
@@ -23,13 +28,13 @@ https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b
23
28
  For a system usage:
24
29
 
25
30
  ```sh
26
- gem install nano-bots -v 0.0.9
31
+ gem install nano-bots -v 0.1.0
27
32
  ```
28
33
 
29
34
  To use it in a project, add it to your `Gemfile`:
30
35
 
31
36
  ```ruby
32
- gem 'nano-bots', '~> 0.0.9'
37
+ gem 'nano-bots', '~> 0.1.0'
33
38
  ```
34
39
 
35
40
  ```sh
@@ -40,8 +45,10 @@ For credentials and configurations, relevant environment variables can be set in
40
45
 
41
46
  ```sh
42
47
  export OPENAI_API_ADDRESS=https://api.openai.com
43
- export OPENAI_API_ACCESS_TOKEN=your-token
44
- export OPENAI_API_USER_IDENTIFIER=your-user
48
+ export OPENAI_API_KEY=your-access-token
49
+
50
+ export NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
51
+ export NANO_BOTS_END_USER=your-user
45
52
 
46
53
  # export NANO_BOTS_STATE_DIRECTORY=/home/user/.local/state/nano-bots
47
54
  # export NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
@@ -51,8 +58,10 @@ Alternatively, if your current directory has a `.env` file with the environment
51
58
 
52
59
  ```sh
53
60
  OPENAI_API_ADDRESS=https://api.openai.com
54
- OPENAI_API_ACCESS_TOKEN=your-token
55
- OPENAI_API_USER_IDENTIFIER=your-user
61
+ OPENAI_API_KEY=your-access-token
62
+
63
+ NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
64
+ NANO_BOTS_END_USER=your-user
56
65
 
57
66
  # NANO_BOTS_STATE_DIRECTORY=/home/user/.local/state/nano-bots
58
67
  # NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
@@ -76,14 +85,15 @@ version: '3.7'
76
85
  services:
77
86
  nano-bots:
78
87
  image: ruby:3.2.2-slim-bullseye
79
- command: sh -c "gem install nano-bots -v 0.0.9 && bash"
88
+ command: sh -c "gem install nano-bots -v 0.1.0 && bash"
80
89
  environment:
81
90
  OPENAI_API_ADDRESS: https://api.openai.com
82
- OPENAI_API_ACCESS_TOKEN: your-token
83
- OPENAI_API_USER_IDENTIFIER: your-user
91
+ OPENAI_API_KEY: your-access-token
92
+ NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
93
+ NANO_BOTS_END_USER: your-user
84
94
  volumes:
85
- - ./your-cartridges:/cartridges
86
- # - ./your-data:/data
95
+ - ./your-cartridges:/.local/share/nano-bots/cartridges
96
+ - ./your-state:/.local/state/nano-bots
87
97
  ```
88
98
 
89
99
  Enter the container:
@@ -96,8 +106,8 @@ Start playing:
96
106
  nb - - eval "hello"
97
107
  nb - - repl
98
108
 
99
- nb cartridges/assistant.yml - eval "hello"
100
- nb cartridges/assistant.yml - repl
109
+ nb assistant.yml - eval "hello"
110
+ nb assistant.yml - repl
101
111
  ```
102
112
 
103
113
  ## Usage
@@ -248,19 +258,129 @@ behaviors:
248
258
  directive: You are a helpful assistant.
249
259
 
250
260
  provider:
251
- name: openai
261
+ id: openai
262
+ credentials:
263
+ address: ENV/OPENAI_API_ADDRESS
264
+ access-token: ENV/OPENAI_API_KEY
252
265
  settings:
266
+ user: ENV/NANO_BOTS_END_USER
253
267
  model: gpt-3.5-turbo
254
- credentials:
255
- address: ENV/OPENAI_API_ADDRESS
256
- access-token: ENV/OPENAI_API_ACCESS_TOKEN
257
- user-identifier: ENV/OPENAI_API_USER_IDENTIFIER
258
268
  ```
259
269
 
260
270
  Check the Nano Bots specification to learn more about [how to build cartridges](https://spec.nbots.io/#/README?id=cartridges).
261
271
 
262
272
  Try the [Nano Bots Clinic (Live Editor)](https://clinic.nbots.io) to learn about creating Cartridges.
263
273
 
274
+ ### Marketplace
275
+
276
+ You can explore the Nano Bots [Marketplace](https://nbots.io) to discover new Cartridges that can help you.
277
+
278
+ ## Security and Privacy
279
+
280
+ Each provider will have its own security and privacy policies (e.g. [OpenAI Policy](https://openai.com/policies/api-data-usage-policies)), so you must consult them to understand their implications.
281
+
282
+ ### Cryptography
283
+
284
+ By default, all states stored in your local disk are encrypted.
285
+
286
+ To ensure that the encryption is secure, you need to define a password through the `NANO_BOTS_ENCRYPTION_PASSWORD` environment variable. Otherwise, although the content will be encrypted, anyone would be able to decrypt it without a password.
287
+
288
+ It's important to note that the content shared with providers, despite being transmitted over secure connections (e.g., [HTTPS](https://en.wikipedia.org/wiki/HTTPS)), will be readable by the provider. This is because providers need to operate on the data, which would not be possible if the content was encrypted beyond HTTPS. So, the data stored locally on your system is encrypted, which does not mean that what you share with providers will not be readable by them.
289
+
290
+ To ensure that your encryption and password are configured properly, you can run the following command:
291
+ ```sh
292
+ nb security
293
+ ```
294
+
295
+ Which should return:
296
+ ```text
297
+ ✅ Encryption is enabled and properly working.
298
+ This means that your data is stored in an encrypted format on your disk.
299
+
300
+ ✅ A password is being used for the encrypted content.
301
+ This means that only those who possess the password can decrypt your data.
302
+ ```
303
+
304
+ Alternatively, you can check it at runtime with:
305
+ ```ruby
306
+ require 'nano-bots'
307
+
308
+ NanoBot.security.check
309
+ # => { encryption: true, password: true }
310
+ ```
311
+
312
+ #### End-user IDs
313
+
314
+ A common strategy for deploying Nano Bots to multiple users through APIs or automations is to assign a unique [end-user ID](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids) for each user. This can be useful if any of your users violate the provider's policy due to abusive behavior. By providing the end-user ID, you can unravel that even though the activity originated from your API Key, the actions taken were not your own.
315
+
316
+ You can define custom end-user identifiers in the following way:
317
+
318
+ ```ruby
319
+ NanoBot.new(environment: { NANO_BOTS_END_USER: 'custom-user-a' })
320
+ NanoBot.new(environment: { NANO_BOTS_END_USER: 'custom-user-b' })
321
+ ```
322
+
323
+ Consider that you have the following end-user identifier in your environment:
324
+ ```sh
325
+ NANO_BOTS_END_USER=your-name
326
+ ```
327
+
328
+ Or a configuration in your Cartridge:
329
+ ```yml
330
+ ---
331
+ provider:
332
+ id: openai
333
+ settings:
334
+ user: your-name
335
+ ```
336
+
337
+ The requests will be performed as follows:
338
+
339
+ ```ruby
340
+ NanoBot.new(cartridge: '-')
341
+ # { user: 'your-name' }
342
+
343
+ NanoBot.new(cartridge: '-', environment: { NANO_BOTS_END_USER: 'custom-user-a' })
344
+ # { user: 'custom-user-a' }
345
+
346
+ NanoBot.new(cartridge: '-', environment: { NANO_BOTS_END_USER: 'custom-user-b' })
347
+ # { user: 'custom-user-b' }
348
+ ```
349
+
350
+ Actually, to enhance privacy, neither your user nor your users' identifiers will be shared in this way. Instead, they will be encrypted before being shared with the provider:
351
+
352
+ ```ruby
353
+ 'your-name'
354
+ # _O7OjYUESagb46YSeUeSfSMzoO1Yg0BZqpsAkPg4j62SeNYlgwq3kn51Ob2wmIehoA==
355
+
356
+ 'custom-user-a'
357
+ # _O7OjYUESagb46YSeUeSfSMzoO1Yg0BZJgIXHCBHyADW-rn4IQr-s2RvP7vym8u5tnzYMIs=
358
+
359
+ 'custom-user-b'
360
+ # _O7OjYUESagb46YSeUeSfSMzoO1Yg0BZkjUwCcsh9sVppKvYMhd2qGRvP7vym8u5tnzYMIg=
361
+ ```
362
+
363
+ In this manner, you possess identifiers if required, however, their actual content can only be decrypted by you via your secure password (`NANO_BOTS_ENCRYPTION_PASSWORD`).
364
+
365
+ ## Decrypting
366
+
367
+ To decrypt your encrypted data, once you have properly configured your password, you can simply run:
368
+
369
+ ```ruby
370
+ require 'nano-bots'
371
+
372
+ NanoBot.security.decrypt('_O7OjYUESagb46YSeUeSfSMzoO1Yg0BZqpsAkPg4j62SeNYlgwq3kn51Ob2wmIehoA==')
373
+ # your-name
374
+
375
+ NanoBot.security.decrypt('_O7OjYUESagb46YSeUeSfSMzoO1Yg0BZJgIXHCBHyADW-rn4IQr-s2RvP7vym8u5tnzYMIs=')
376
+ # custom-user-a
377
+
378
+ NanoBot.security.decrypt('_O7OjYUESagb46YSeUeSfSMzoO1Yg0BZkjUwCcsh9sVppKvYMhd2qGRvP7vym8u5tnzYMIg=')
379
+ # custom-user-b
380
+ ```
381
+
382
+ If you lose your password, you lose your data. It is not possible to recover it at all. For real.
383
+
264
384
  ## Providers
265
385
 
266
386
  Currently supported providers:
@@ -288,5 +408,5 @@ gem build nano-bots.gemspec
288
408
 
289
409
  gem signin
290
410
 
291
- gem push nano-bots-0.0.9.gem
411
+ gem push nano-bots-0.1.0.gem
292
412
  ```
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'rbnacl'
5
+ require 'base64'
6
+
7
+ module NanoBot
8
+ module Components
9
+ class Crypto
10
+ include Singleton
11
+
12
+ def initialize
13
+ password = ENV.fetch('NANO_BOTS_ENCRYPTION_PASSWORD', nil)
14
+
15
+ password = 'UNSAFE' unless password && password != ''
16
+
17
+ @box = RbNaCl::SecretBox.new(RbNaCl::Hash.sha256(password))
18
+ @fixed_nonce = RbNaCl::Hash.sha256(password)[0...@box.nonce_bytes]
19
+ end
20
+
21
+ def encrypt(content, soft: false)
22
+ nonce = soft ? @fixed_nonce : RbNaCl::Random.random_bytes(@box.nonce_bytes)
23
+ Base64.urlsafe_encode64(nonce + @box.encrypt(nonce, content))
24
+ end
25
+
26
+ def decrypt(content)
27
+ decoded_content = Base64.urlsafe_decode64(content)
28
+ nonce = decoded_content[0...@box.nonce_bytes]
29
+ cipher_text = decoded_content[@box.nonce_bytes..]
30
+
31
+ @box.decrypt(nonce, cipher_text)
32
+ end
33
+
34
+ def self.encrypt(content, soft: false)
35
+ instance.encrypt(content, soft:)
36
+ end
37
+
38
+ def self.decrypt(content)
39
+ instance.decrypt(content)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -7,12 +7,12 @@ require_relative './providers/openai'
7
7
  module NanoBot
8
8
  module Components
9
9
  class Provider
10
- def self.new(provider)
11
- case provider[:name]
10
+ def self.new(provider, environment: {})
11
+ case provider[:id]
12
12
  when 'openai'
13
- Providers::OpenAI.new(provider[:settings])
13
+ Providers::OpenAI.new(provider[:settings], provider[:credentials], environment:)
14
14
  else
15
- raise "Unsupported provider #{provider[:name]}"
15
+ raise "Unsupported provider \"#{provider[:id]}\""
16
16
  end
17
17
  end
18
18
  end
@@ -6,6 +6,10 @@ module NanoBot
6
6
  module Components
7
7
  module Providers
8
8
  class Base
9
+ def initialize(_settings, _credentials, _environment: {})
10
+ raise NoMethodError, "The 'initialize' method is not implemented for the current provider."
11
+ end
12
+
9
13
  def evaluate(_payload)
10
14
  raise NoMethodError, "The 'evaluate' method is not implemented for the current provider."
11
15
  end
@@ -3,11 +3,14 @@
3
3
  require 'openai'
4
4
 
5
5
  require_relative './base'
6
+ require_relative '../crypto'
6
7
 
7
8
  module NanoBot
8
9
  module Components
9
10
  module Providers
10
11
  class OpenAI < Base
12
+ DEFAULT_ADDRESS = 'https://api.openai.com'
13
+
11
14
  CHAT_SETTINGS = %i[
12
15
  model stream temperature top_p n stop max_tokens
13
16
  presence_penalty frequency_penalty logit_bias
@@ -15,13 +18,18 @@ module NanoBot
15
18
 
16
19
  attr_reader :settings
17
20
 
18
- def initialize(settings)
21
+ def initialize(settings, credentials, environment: {})
19
22
  @settings = settings
23
+ @credentials = credentials
24
+ @environment = environment
20
25
 
21
- @client = ::OpenAI::Client.new(
22
- uri_base: "#{@settings[:credentials][:address].sub(%r{/$}, '')}/",
23
- access_token: @settings[:credentials][:'access-token']
24
- )
26
+ uri_base = if @credentials[:address].nil? || @credentials[:address].to_s.strip.empty?
27
+ "#{DEFAULT_ADDRESS}/"
28
+ else
29
+ "#{@credentials[:address].to_s.sub(%r{/$}, '')}/"
30
+ end
31
+
32
+ @client = ::OpenAI::Client.new(uri_base:, access_token: @credentials[:'access-token'])
25
33
  end
26
34
 
27
35
  def stream(input)
@@ -46,11 +54,7 @@ module NanoBot
46
54
  )
47
55
  end
48
56
 
49
- payload = {
50
- model: @settings[:model],
51
- user: @settings[:credentials][:'user-identifier'],
52
- messages:
53
- }
57
+ payload = { user: OpenAI.end_user(@settings, @environment), messages: }
54
58
 
55
59
  CHAT_SETTINGS.each do |key|
56
60
  payload[key] = @settings[key] if @settings.key?(key)
@@ -80,6 +84,27 @@ module NanoBot
80
84
  block.call({ who: 'AI', message: result.dig('choices', 0, 'message', 'content') }, true)
81
85
  end
82
86
  end
87
+
88
+ def self.end_user(settings, environment)
89
+ user = ENV.fetch('NANO_BOTS_END_USER', nil)
90
+
91
+ user = settings[:user] if !settings[:user].nil? && !settings[:user].to_s.strip.empty?
92
+
93
+ candidate = environment && (
94
+ environment['NANO_BOTS_END_USER'] ||
95
+ environment[:NANO_BOTS_END_USER]
96
+ )
97
+
98
+ user = candidate if !candidate.nil? && !candidate.to_s.strip.empty?
99
+
100
+ user = if user.nil? || user.to_s.strip.empty?
101
+ 'unknown'
102
+ else
103
+ user.to_s.strip
104
+ end
105
+
106
+ Crypto.encrypt(user, soft: true)
107
+ end
83
108
  end
84
109
  end
85
110
  end
@@ -3,11 +3,37 @@
3
3
  require 'babosa'
4
4
 
5
5
  require_relative '../logic/helpers/hash'
6
+ require_relative './crypto'
6
7
 
7
8
  module NanoBot
8
9
  module Components
9
10
  class Storage
10
- def self.build_path_and_ensure_state_file!(key, cartridge)
11
+ def self.end_user(cartridge, environment)
12
+ user = ENV.fetch('NANO_BOTS_END_USER', nil)
13
+
14
+ if cartridge[:provider][:id] == 'openai' &&
15
+ !cartridge[:provider][:settings][:user].nil? &&
16
+ !cartridge[:provider][:settings][:user].to_s.strip.empty?
17
+ user = cartridge[:provider][:settings][:user]
18
+ end
19
+
20
+ candidate = environment && (
21
+ environment['NANO_BOTS_END_USER'] ||
22
+ environment[:NANO_BOTS_END_USER]
23
+ )
24
+
25
+ user = candidate if !candidate.nil? && !candidate.to_s.strip.empty?
26
+
27
+ user = if user.nil? || user.to_s.strip.empty?
28
+ 'unknown'
29
+ else
30
+ user.to_s.strip
31
+ end
32
+
33
+ Crypto.encrypt(user, soft: true)
34
+ end
35
+
36
+ def self.build_path_and_ensure_state_file!(key, cartridge, environment: {})
11
37
  path = [
12
38
  Logic::Helpers::Hash.fetch(cartridge, %i[state directory]),
13
39
  ENV.fetch('NANO_BOTS_STATE_DIRECTORY', nil)
@@ -17,14 +43,23 @@ module NanoBot
17
43
 
18
44
  path = "#{user_home!.sub(%r{/$}, '')}/.local/state/nano-bots" if path.nil?
19
45
 
20
- path = "#{path.sub(%r{/$}, '')}/ruby-nano-bots/#{cartridge[:meta][:author].to_slug.normalize}"
46
+ path = "#{path.sub(%r{/$}, '')}/ruby-nano-bots"
47
+
48
+ path = "#{path}/#{cartridge[:meta][:author].to_slug.normalize}"
21
49
  path = "#{path}/#{cartridge[:meta][:name].to_slug.normalize}"
22
- path = "#{path}/#{cartridge[:meta][:version].to_s.gsub('.', '-').to_slug.normalize}/#{key}"
50
+ path = "#{path}/#{cartridge[:meta][:version].to_s.gsub('.', '-').to_slug.normalize}"
51
+ path = "#{path}/#{end_user(cartridge, environment)}"
52
+ path = "#{path}/#{Crypto.encrypt(key, soft: true)}"
23
53
  path = "#{path}/state.json"
24
54
 
25
55
  FileUtils.mkdir_p(File.dirname(path))
26
56
 
27
- File.write(path, JSON.generate({ key:, history: [] })) unless File.exist?(path)
57
+ unless File.exist?(path)
58
+ File.write(
59
+ path,
60
+ Crypto.encrypt(JSON.generate({ key:, history: [] }))
61
+ )
62
+ end
28
63
 
29
64
  path
30
65
  end
@@ -39,7 +39,7 @@ module NanoBot
39
39
  rescue StandardError => _e
40
40
  end
41
41
 
42
- cartridges.sort_by { |cartridge| cartridge[:meta][:name] }
42
+ cartridges = cartridges.sort_by { |cartridge| cartridge[:meta][:name] }
43
43
 
44
44
  cartridges.prepend(
45
45
  { system: { id: '-' }, meta: { name: 'Default', symbol: '🤖' } }
@@ -13,14 +13,16 @@ require_relative './session'
13
13
  module NanoBot
14
14
  module Controllers
15
15
  class Instance
16
- def initialize(cartridge_path:, stream:, state: nil)
16
+ def initialize(cartridge_path:, stream:, state: nil, environment: {})
17
17
  @stream = stream
18
18
 
19
19
  load_cartridge!(cartridge_path)
20
20
 
21
- provider = Components::Provider.new(@cartridge[:provider])
21
+ provider = Components::Provider.new(@cartridge[:provider], environment:)
22
22
 
23
- @session = Session.new(provider:, cartridge: @cartridge, state:, stream: @stream)
23
+ @session = Session.new(
24
+ provider:, cartridge: @cartridge, state:, stream: @stream, environment:
25
+ )
24
26
  end
25
27
 
26
28
  def cartridge
@@ -11,6 +11,28 @@ module NanoBot
11
11
  case ARGV[0]
12
12
  when 'version'
13
13
  puts NanoBot::GEM[:version]
14
+ exit
15
+ when 'security'
16
+ result = NanoBot.security.check
17
+
18
+ if result[:encryption]
19
+ puts "\n✅ Encryption is enabled and properly working."
20
+ puts ' This means that your data is stored in an encrypted format on your disk.'
21
+ else
22
+ puts "\n❌ Encryption is not being utilized to store your content."
23
+ puts ' This means that your data can be easily read because it is stored in plaintext.'
24
+ end
25
+
26
+ if result[:password]
27
+ puts "\n✅ A password is being used for the encrypted content."
28
+ puts ' This means that only those who possess the password can decrypt your data.'
29
+ else
30
+ puts "\n❌ No custom password is being used for the encrypted content."
31
+ puts ' This means that anyone can easily decrypt your data.'
32
+ end
33
+
34
+ puts ''
35
+
14
36
  exit
15
37
  when 'help', '', nil
16
38
  puts ''
@@ -34,6 +56,7 @@ module NanoBot
34
56
  puts ' nb - STATE-KEY state'
35
57
  puts ' nb cartridge.yml STATE-KEY state'
36
58
  puts ''
59
+ puts ' nb security'
37
60
  puts ' nb version'
38
61
  puts ' nb help'
39
62
  puts ''
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pry'
4
- require 'rainbow'
5
-
6
- require_relative '../../logic/helpers/hash'
7
3
  require_relative '../../logic/cartridge/affixes'
8
4
 
9
5
  module NanoBot
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../components/crypto'
4
+
5
+ module NanoBot
6
+ module Controllers
7
+ module Security
8
+ def self.decrypt(content)
9
+ Components::Crypto.decrypt(content)
10
+ end
11
+
12
+ def self.encrypt(content, soft: false)
13
+ Components::Crypto.encrypt(content, soft:)
14
+ end
15
+
16
+ def self.check
17
+ password = ENV.fetch('NANO_BOTS_ENCRYPTION_PASSWORD', nil)
18
+ password = 'UNSAFE' unless password && password != ''
19
+
20
+ {
21
+ encryption: (
22
+ Components::Crypto.encrypt('SAFE') != 'SAFE' &&
23
+ Components::Crypto.encrypt('SAFE') != Components::Crypto.encrypt('SAFE') &&
24
+ Components::Crypto.decrypt(Components::Crypto.encrypt('SAFE')) == 'SAFE'
25
+ ),
26
+ password: password != 'UNSAFE'
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -9,6 +9,7 @@ require_relative '../logic/cartridge/streaming'
9
9
  require_relative '../logic/cartridge/interaction'
10
10
  require_relative '../components/storage'
11
11
  require_relative '../components/adapter'
12
+ require_relative '../components/crypto'
12
13
 
13
14
  module NanoBot
14
15
  module Controllers
@@ -17,7 +18,7 @@ module NanoBot
17
18
  class Session
18
19
  attr_accessor :stream
19
20
 
20
- def initialize(provider:, cartridge:, state: nil, stream: $stdout)
21
+ def initialize(provider:, cartridge:, state: nil, stream: $stdout, environment: {})
21
22
  @stream = stream
22
23
  @provider = provider
23
24
  @cartridge = cartridge
@@ -28,8 +29,9 @@ module NanoBot
28
29
  @state = { history: [] }
29
30
  else
30
31
  @state_path = Components::Storage.build_path_and_ensure_state_file!(
31
- state.strip, @cartridge
32
+ state.strip, @cartridge, environment:
32
33
  )
34
+
33
35
  @state = load_state
34
36
  end
35
37
  end
@@ -39,11 +41,13 @@ module NanoBot
39
41
  end
40
42
 
41
43
  def load_state
42
- @state = Logic::Helpers::Hash.symbolize_keys(JSON.parse(File.read(@state_path)))
44
+ @state = Logic::Helpers::Hash.symbolize_keys(JSON.parse(
45
+ Components::Crypto.decrypt(File.read(@state_path))
46
+ ))
43
47
  end
44
48
 
45
49
  def store_state!
46
- File.write(@state_path, JSON.generate(@state))
50
+ File.write(@state_path, Components::Crypto.encrypt(JSON.generate(@state)))
47
51
  end
48
52
 
49
53
  def boot(mode:)
@@ -3,11 +3,12 @@ version: '3.7'
3
3
  services:
4
4
  nano-bots:
5
5
  image: ruby:3.2.2-slim-bullseye
6
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev lua5.4-dev && gem install nano-bots -v 0.0.9 && bash"
6
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev && gem install nano-bots -v 0.1.0 && bash"
7
7
  environment:
8
8
  OPENAI_API_ADDRESS: https://api.openai.com
9
- OPENAI_API_ACCESS_TOKEN: your-token
10
- OPENAI_API_USER_IDENTIFIER: your-user
9
+ OPENAI_API_KEY: your-access-token
10
+ NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
11
+ NANO_BOTS_END_USER: your-user
11
12
  volumes:
12
- - ./your-cartridges:/cartridges
13
- # - ./your-data:/data
13
+ - ./your-cartridges:/.local/share/nano-bots/cartridges
14
+ - ./your-state:/.local/state/nano-bots
data/nano-bots.gemspec CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency 'faraday', '~> 2.7', '>= 2.7.5'
37
37
  spec.add_dependency 'pry', '~> 0.14.2'
38
38
  spec.add_dependency 'rainbow', '~> 3.1', '>= 3.1.1'
39
+ spec.add_dependency 'rbnacl', '~> 7.1', '>= 7.1.1'
39
40
  spec.add_dependency 'ruby-openai', '~> 4.0'
40
41
  spec.add_dependency 'sweet-moon', '~> 0.0.7'
41
42
 
@@ -5,12 +5,22 @@ require 'dotenv/load'
5
5
  require_relative '../../static/gem'
6
6
  require_relative '../../controllers/cartridges'
7
7
  require_relative '../../controllers/instance'
8
+ require_relative '../../controllers/security'
8
9
  require_relative '../../controllers/interfaces/cli'
9
10
  require_relative '../../components/stream'
10
11
 
11
12
  module NanoBot
12
- def self.new(cartridge: '-', state: '-')
13
- Controllers::Instance.new(cartridge_path: cartridge, state:, stream: Components::Stream.new)
13
+ def self.new(cartridge: '-', state: '-', environment: {})
14
+ Controllers::Instance.new(
15
+ cartridge_path: cartridge,
16
+ state:,
17
+ stream: Components::Stream.new,
18
+ environment:
19
+ )
20
+ end
21
+
22
+ def self.security
23
+ Controllers::Security
14
24
  end
15
25
 
16
26
  def self.cartridges
@@ -21,8 +31,10 @@ module NanoBot
21
31
  Controllers::Interfaces::CLI.handle!
22
32
  end
23
33
 
24
- def self.repl(cartridge: '-', state: '-')
25
- Controllers::Instance.new(cartridge_path: cartridge, state:, stream: $stdout).repl
34
+ def self.repl(cartridge: '-', state: '-', environment: {})
35
+ Controllers::Instance.new(
36
+ cartridge_path: cartridge, state:, stream: $stdout, environment:
37
+ ).repl
26
38
  end
27
39
 
28
40
  def self.version
@@ -1,14 +1,17 @@
1
1
  ---
2
2
  meta:
3
+ symbol: 🤖
3
4
  name: Unknown
4
- author: Nobody
5
+ author: None
5
6
  version: 0.0.0
7
+ license: CC0-1.0
8
+ description: Unknown
6
9
 
7
10
  provider:
8
- name: openai
11
+ id: openai
12
+ credentials:
13
+ address: ENV/OPENAI_API_ADDRESS
14
+ access-token: ENV/OPENAI_API_KEY
9
15
  settings:
16
+ user: ENV/NANO_BOTS_END_USER
10
17
  model: gpt-3.5-turbo
11
- credentials:
12
- address: ENV/OPENAI_API_ADDRESS
13
- access-token: ENV/OPENAI_API_ACCESS_TOKEN
14
- user-identifier: ENV/OPENAI_API_USER_IDENTIFIER
@@ -8,7 +8,6 @@ interfaces:
8
8
  prompt:
9
9
  - text: '🤖'
10
10
  - text: '> '
11
- color: blue
12
11
  eval:
13
12
  output:
14
13
  stream: true
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright © 2016-2022 Calvin Rose and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/static/gem.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module NanoBot
4
4
  GEM = {
5
5
  name: 'nano-bots',
6
- version: '0.0.9',
6
+ version: '0.1.0',
7
7
  author: 'icebaker',
8
8
  summary: 'Ruby Implementation of Nano Bots: small, AI-powered bots',
9
9
  description: 'Ruby Implementation of Nano Bots: small, AI-powered bots easily shared as a single file, designed to support multiple providers such as Vicuna, OpenAI ChatGPT, Google PaLM, Alpaca, and LLaMA.',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nano-bots
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - icebaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-27 00:00:00.000000000 Z
11
+ date: 2023-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: babosa
@@ -98,6 +98,26 @@ dependencies:
98
98
  - - ">="
99
99
  - !ruby/object:Gem::Version
100
100
  version: 3.1.1
101
+ - !ruby/object:Gem::Dependency
102
+ name: rbnacl
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '7.1'
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 7.1.1
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '7.1'
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 7.1.1
101
121
  - !ruby/object:Gem::Dependency
102
122
  name: ruby-openai
103
123
  requirement: !ruby/object:Gem::Requirement
@@ -144,6 +164,7 @@ files:
144
164
  - README.md
145
165
  - bin/nb
146
166
  - components/adapter.rb
167
+ - components/crypto.rb
147
168
  - components/provider.rb
148
169
  - components/providers/base.rb
149
170
  - components/providers/openai.rb
@@ -154,6 +175,7 @@ files:
154
175
  - controllers/interfaces/cli.rb
155
176
  - controllers/interfaces/eval.rb
156
177
  - controllers/interfaces/repl.rb
178
+ - controllers/security.rb
157
179
  - controllers/session.rb
158
180
  - docker-compose.example.yml
159
181
  - logic/cartridge/adapters.rb
@@ -167,6 +189,7 @@ files:
167
189
  - ports/dsl/nano-bots/cli.rb
168
190
  - static/cartridges/baseline.yml
169
191
  - static/cartridges/default.yml
192
+ - static/fennel/LICENSE
170
193
  - static/fennel/fennel.lua
171
194
  - static/gem.rb
172
195
  homepage: https://github.com/icebaker/ruby-nano-bots