baseh 1.1.0 → 2.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.
data/lib/baseh.rb CHANGED
@@ -10,54 +10,132 @@ require_relative "baseh/checksum"
10
10
  require_relative "baseh/feistel"
11
11
  require_relative "baseh/profiles"
12
12
  require_relative "baseh/baseh"
13
- require_relative "baseh/zero"
14
13
 
15
14
  # baseH (Human Reference Code) codec. See spec/IMPLEMENTATION_CODEC.md in the
16
15
  # repository root for the normative specification.
17
16
  module Baseh
17
+ # The frozen published permutation key used by every plain tier helper.
18
+ # Public by design: it makes issued codes look non-sequential but offers no
19
+ # secrecy, since anyone can read it here. Never swap it on a live
20
+ # namespace; codes only decode with the key they were issued under. Use
21
+ # the -p helpers to supply private key material.
22
+ FROZEN_KEY_BYTES = Profiles::FROZEN_KEY_BYTES
23
+
18
24
  class << self
19
25
  # Frozen tier baseh-minimum-v1: alphanumeric with no strips, no checksum,
20
- # hyphen-delimited XXX-XXX. Permutation off.
26
+ # hyphen-delimited XXX-XXX. Permutes with the frozen published key.
21
27
  def baseh_minimum_v1
22
28
  Profiles.baseh_minimum_v1
23
29
  end
24
30
 
25
- # baseh-minimum with feistel-v1 permutation. key_bytes is required.
31
+ # baseh-minimum permuted with caller-supplied key material. key_bytes is required.
26
32
  def baseh_minimum_p_v1(key_bytes:, key_id: "default", rounds: 8)
27
33
  Profiles.baseh_minimum_p_v1(key_bytes: key_bytes, key_id: key_id, rounds: rounds)
28
34
  end
29
35
 
30
- # Frozen tier baseh-light-v1: visual light plus spoken light, one
31
- # checksum symbol. Permutation off.
36
+ # Frozen tier baseh-light-v1: visual light plus spoken light, two
37
+ # checksum symbols, hyphen-delimited. Permutes with the frozen published
38
+ # key.
32
39
  def baseh_light_v1
33
40
  Profiles.baseh_light_v1
34
41
  end
35
42
 
36
- # baseh-light with feistel-v1 permutation. key_bytes is required.
43
+ # baseh-light permuted with caller-supplied key material. key_bytes is required.
37
44
  def baseh_light_p_v1(key_bytes:, key_id: "default", rounds: 8)
38
45
  Profiles.baseh_light_p_v1(key_bytes: key_bytes, key_id: key_id, rounds: rounds)
39
46
  end
40
47
 
41
- # Frozen tier baseh-medium-v1: visual medium plus spoken medium, one
42
- # checksum symbol. The default. Permutation off.
48
+ # Frozen tier baseh-medium-v1: visual medium plus spoken medium, two
49
+ # checksum symbols, hyphen-delimited. The default. Permutes with the
50
+ # frozen published key.
43
51
  def baseh_medium_v1
44
52
  Profiles.baseh_medium_v1
45
53
  end
46
54
 
47
- # baseh-medium with feistel-v1 permutation. key_bytes is required.
55
+ # baseh-medium permuted with caller-supplied key material. key_bytes is required.
48
56
  def baseh_medium_p_v1(key_bytes:, key_id: "default", rounds: 8)
49
57
  Profiles.baseh_medium_p_v1(key_bytes: key_bytes, key_id: key_id, rounds: rounds)
50
58
  end
51
59
 
52
60
  # Frozen tier baseh-heavy-v1: conservative alphabet plus spoken heavy,
53
- # one checksum symbol. Permutation off.
61
+ # two checksum symbols, hyphen-delimited. Permutes with the frozen
62
+ # published key.
54
63
  def baseh_heavy_v1
55
64
  Profiles.baseh_heavy_v1
56
65
  end
57
66
 
58
- # baseh-heavy with feistel-v1 permutation. key_bytes is required.
67
+ # baseh-heavy permuted with caller-supplied key material. key_bytes is required.
59
68
  def baseh_heavy_p_v1(key_bytes:, key_id: "default", rounds: 8)
60
69
  Profiles.baseh_heavy_p_v1(key_bytes: key_bytes, key_id: key_id, rounds: rounds)
61
70
  end
71
+
72
+ # Frozen tier baseh-expandable-v1: variable-length codes, four characters
73
+ # while the namespace is small and growing one symbol per generation.
74
+ # Permutes per generation with the frozen published key.
75
+ def baseh_expandable_v1
76
+ Profiles.baseh_expandable_v1
77
+ end
78
+
79
+ # baseh-expandable permuted with caller-supplied key material. key_bytes is required.
80
+ def baseh_expandable_p_v1(key_bytes:, key_id: "default", rounds: 8)
81
+ Profiles.baseh_expandable_p_v1(key_bytes: key_bytes, key_id: key_id, rounds: rounds)
82
+ end
83
+
84
+ # Zero-config facade over the frozen baseh-expandable-v1 profile, the
85
+ # recommended default for new namespaces. Most applications never need a
86
+ # profile object at all: these methods share one lazily constructed,
87
+ # stateless codec instance (thread-safe once built, per Baseh::Baseh).
88
+ #
89
+ # Baseh.encode(123456) -> String canonical code
90
+ # Baseh.decode(code).id -> 123456
91
+ # Baseh.validate(code).valid -> true/false
92
+ #
93
+ # decode returns the full DecodeResult (id, canonical_code, corrected)
94
+ # and validate returns the full ValidateResult (valid, canonical_code,
95
+ # reason), exactly as the instance API does.
96
+
97
+ # Encode an identifier with the default expandable profile.
98
+ #
99
+ # @param id [Integer] any non-negative id
100
+ # @return [String] canonical code
101
+ # @raise [BasehError] OUT_OF_RANGE, BLOCKED_CODE
102
+ def encode(id)
103
+ default.encode(id: id)
104
+ end
105
+
106
+ # Decode a code from the default expandable profile.
107
+ #
108
+ # @param input [String]
109
+ # @param options keyword options of Baseh::Baseh#decode (accept_spaces:,
110
+ # try_correction:, confusion_profile:, max_corrections:)
111
+ # @return [Baseh::Baseh::DecodeResult]
112
+ # @raise [BasehError] same codes as the instance decode
113
+ def decode(input, **options)
114
+ default.decode(input, **options)
115
+ end
116
+
117
+ # Live as-you-type inspection of input against the default expandable
118
+ # profile (spec 12.5). Never raises on user input.
119
+ #
120
+ # @param input [String]
121
+ # @return [Baseh::Baseh::InspectResult]
122
+ def inspect(input)
123
+ default.inspect(input)
124
+ end
125
+
126
+ # Validate a code against the default expandable profile without raising.
127
+ #
128
+ # @param input [String]
129
+ # @param options keyword options of Baseh::Baseh#validate (the same
130
+ # keyword options decode accepts)
131
+ # @return [Baseh::Baseh::ValidateResult]
132
+ def validate(input, **options)
133
+ default.validate(input, **options)
134
+ end
135
+
136
+ # The shared default-profile codec, built on first use.
137
+ def default
138
+ @default ||= Baseh.new(Profiles.baseh_expandable_v1)
139
+ end
62
140
  end
63
141
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: baseh
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - cloudyventures
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-01 00:00:00.000000000 Z
11
+ date: 2026-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -28,6 +28,7 @@ description: 'Encodes and decodes human reference codes per the baseH codec spec
28
28
  fixed-length base-N bodies, rolling polynomial checksums, optional feistel-v1 permutation,
29
29
  spoken-confusion correction and profanity safety.'
30
30
  email:
31
+ - mat@cloudyventures.com
31
32
  executables: []
32
33
  extensions: []
33
34
  extra_rdoc_files: []
@@ -43,12 +44,13 @@ files:
43
44
  - lib/baseh/profile.rb
44
45
  - lib/baseh/profiles.rb
45
46
  - lib/baseh/version.rb
46
- - lib/baseh/zero.rb
47
- homepage:
47
+ homepage: https://github.com/cloudyventures/baseh
48
48
  licenses:
49
49
  - AGPL-3.0
50
50
  metadata:
51
51
  rubygems_mfa_required: 'true'
52
+ source_code_uri: https://github.com/cloudyventures/baseh
53
+ bug_tracker_uri: https://github.com/cloudyventures/baseh/issues
52
54
  post_install_message:
53
55
  rdoc_options: []
54
56
  require_paths:
data/lib/baseh/zero.rb DELETED
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Baseh
4
- # Zero-config pair over the frozen baseh-medium-v1 profile. No profile
5
- # object, no key: just the two functions an application needs when it
6
- # does not want to think about configuration.
7
- #
8
- # Baseh.to_code(481890303) -> "ZZZZZZV"
9
- # Baseh.from_code("ZZZZZZV") -> 481890303
10
- #
11
- # to_code accepts an Integer or a decimal string of digits. from_code
12
- # strips every whitespace character (edges and internal), accepts
13
- # lowercase and the typed aliases (O, I, L) and returns the id as an
14
- # Integer. Any invalid input raises BasehError, including the rare
15
- # BLOCKED_CODE identifiers that spell a blocklisted word; no correction
16
- # attempts are ever made.
17
- module Zero
18
- DECIMAL = /\A[0-9]+\z/.freeze
19
- WHITESPACE = /\s+/.freeze
20
-
21
- ZERO = Baseh.new(Profiles.baseh_medium_v1)
22
-
23
- module_function
24
-
25
- # Encode an identifier with the zero-config Medium profile.
26
- #
27
- # @param id [Integer, String] Integer or decimal string of digits
28
- # @return [String] canonical code
29
- # @raise [ArgumentError] when id is neither an Integer nor a decimal string
30
- # @raise [BasehError] OUT_OF_RANGE, BLOCKED_CODE
31
- def to_code(id)
32
- value =
33
- case id
34
- when Integer then id
35
- when String
36
- if DECIMAL.match?(id)
37
- id.to_i
38
- else
39
- raise ArgumentError,
40
- "to_code expects a non-negative Integer or a decimal string"
41
- end
42
- else
43
- raise ArgumentError,
44
- "to_code expects a non-negative Integer or a decimal string"
45
- end
46
- ZERO.encode(id: value)
47
- end
48
-
49
- # Decode a code from the zero-config Medium profile back to its id.
50
- #
51
- # @param code [String]
52
- # @return [Integer]
53
- # @raise [BasehError] INVALID_LENGTH, INVALID_CHARACTER, INVALID_CHECKSUM
54
- def from_code(code)
55
- input = code.is_a?(String) ? code.gsub(WHITESPACE, "") : code
56
- ZERO.decode(input).id
57
- end
58
- end
59
-
60
- class << self
61
- # See Baseh::Zero.to_code.
62
- def to_code(id)
63
- Zero.to_code(id)
64
- end
65
-
66
- # See Baseh::Zero.from_code.
67
- def from_code(code)
68
- Zero.from_code(code)
69
- end
70
- end
71
- end