diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/crypto.rb | 47 | ||||
-rw-r--r-- | components/provider.rb | 4 | ||||
-rw-r--r-- | components/providers/openai.rb | 19 | ||||
-rw-r--r-- | components/storage.rb | 33 |
4 files changed, 91 insertions, 12 deletions
diff --git a/components/crypto.rb b/components/crypto.rb new file mode 100644 index 0000000..3f97f57 --- /dev/null +++ b/components/crypto.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'singleton' +require 'rbnacl' +require 'base64' + +module NanoBot + module Components + class Crypto + include Singleton + + def initialize + password = ENV.fetch('NANO_BOTS_ENCRYPTION_PASSWORD', nil) + + password = 'UNSAFE' unless password && password != '' + + @box = RbNaCl::SecretBox.new(RbNaCl::Hash.sha256(password)) + @fixed_nonce = RbNaCl::Hash.sha256(password)[0...@box.nonce_bytes] + end + + def encrypt(content, soft: false) + return content unless @box + + nonce = soft ? @fixed_nonce : RbNaCl::Random.random_bytes(@box.nonce_bytes) + Base64.urlsafe_encode64(nonce + @box.encrypt(nonce, content)) + end + + def decrypt(content) + return content unless @box + + decoded_content = Base64.urlsafe_decode64(content) + nonce = decoded_content[0...@box.nonce_bytes] + cipher_text = decoded_content[@box.nonce_bytes..] + + @box.decrypt(nonce, cipher_text) + end + + def self.encrypt(content, soft: false) + instance.encrypt(content, soft:) + end + + def self.decrypt(content) + instance.decrypt(content) + end + end + end +end diff --git a/components/provider.rb b/components/provider.rb index dbfc8bd..3138cc4 100644 --- a/components/provider.rb +++ b/components/provider.rb @@ -7,10 +7,10 @@ require_relative './providers/openai' module NanoBot module Components class Provider - def self.new(provider) + def self.new(provider, environment: {}) case provider[:name] when 'openai' - Providers::OpenAI.new(provider[:settings]) + Providers::OpenAI.new(provider[:settings], environment:) else raise "Unsupported provider #{provider[:name]}" end diff --git a/components/providers/openai.rb b/components/providers/openai.rb index c0a6639..c64a588 100644 --- a/components/providers/openai.rb +++ b/components/providers/openai.rb @@ -3,6 +3,7 @@ require 'openai' require_relative './base' +require_relative '../crypto' module NanoBot module Components @@ -15,8 +16,9 @@ module NanoBot attr_reader :settings - def initialize(settings) + def initialize(settings, environment: {}) @settings = settings + @environment = environment @client = ::OpenAI::Client.new( uri_base: "#{@settings[:credentials][:address].sub(%r{/$}, '')}/", @@ -46,11 +48,16 @@ module NanoBot ) end - payload = { - model: @settings[:model], - user: @settings[:credentials][:'user-identifier'], - messages: - } + user = @settings[:credentials][:'user-identifier'] + + user_suffix = @environment && ( + @environment['NANO_BOTS_USER_IDENTIFIER'] || + @environment[:NANO_BOTS_USER_IDENTIFIER] + ) + + user = "#{user}/#{user_suffix}" if user_suffix && user_suffix != '' + + payload = { model: @settings[:model], user: Crypto.encrypt(user, soft: true), messages: } CHAT_SETTINGS.each do |key| payload[key] = @settings[key] if @settings.key?(key) diff --git a/components/storage.rb b/components/storage.rb index cba3dd0..b6d8910 100644 --- a/components/storage.rb +++ b/components/storage.rb @@ -3,11 +3,12 @@ require 'babosa' require_relative '../logic/helpers/hash' +require_relative './crypto' module NanoBot module Components class Storage - def self.build_path_and_ensure_state_file!(key, cartridge) + def self.build_path_and_ensure_state_file!(key, cartridge, environment: {}) path = [ Logic::Helpers::Hash.fetch(cartridge, %i[state directory]), ENV.fetch('NANO_BOTS_STATE_DIRECTORY', nil) @@ -17,14 +18,38 @@ module NanoBot path = "#{user_home!.sub(%r{/$}, '')}/.local/state/nano-bots" if path.nil? - path = "#{path.sub(%r{/$}, '')}/ruby-nano-bots/#{cartridge[:meta][:author].to_slug.normalize}" + prefix = environment && ( + environment['NANO_BOTS_USER_IDENTIFIER'] || + environment[:NANO_BOTS_USER_IDENTIFIER] + ) + + path = "#{path.sub(%r{/$}, '')}/ruby-nano-bots/vault" + + if prefix + normalized = prefix.split('/').map do |part| + Crypto.encrypt( + part.to_s.gsub('.', '-').force_encoding('UTF-8').to_slug.normalize, + soft: true + ) + end.join('/') + + path = "#{path}/#{normalized}" + end + + path = "#{path}/#{cartridge[:meta][:author].to_slug.normalize}" path = "#{path}/#{cartridge[:meta][:name].to_slug.normalize}" - path = "#{path}/#{cartridge[:meta][:version].to_s.gsub('.', '-').to_slug.normalize}/#{key}" + path = "#{path}/#{cartridge[:meta][:version].to_s.gsub('.', '-').to_slug.normalize}" + path = "#{path}/#{Crypto.encrypt(key, soft: true)}" path = "#{path}/state.json" FileUtils.mkdir_p(File.dirname(path)) - File.write(path, JSON.generate({ key:, history: [] })) unless File.exist?(path) + unless File.exist?(path) + File.write( + path, + Crypto.encrypt(JSON.generate({ key:, history: [] })) + ) + end path end |