familia 2.0.0.pre25 → 2.0.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.rst +69 -0
  4. data/Gemfile +1 -0
  5. data/Gemfile.lock +2 -2
  6. data/README.md +1 -3
  7. data/docs/guides/feature-encrypted-fields.md +1 -1
  8. data/docs/guides/feature-expiration.md +1 -1
  9. data/docs/guides/feature-quantization.md +1 -1
  10. data/docs/overview.md +7 -7
  11. data/docs/reference/api-technical.md +103 -7
  12. data/familia.gemspec +1 -2
  13. data/lib/familia/data_type/types/hashkey.rb +238 -0
  14. data/lib/familia/data_type/types/listkey.rb +110 -4
  15. data/lib/familia/data_type/types/sorted_set.rb +365 -0
  16. data/lib/familia/data_type/types/stringkey.rb +139 -0
  17. data/lib/familia/data_type/types/unsorted_set.rb +122 -2
  18. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +2 -1
  19. data/lib/familia/features/relationships/participation/through_model_operations.rb +4 -3
  20. data/lib/familia/features/relationships/participation.rb +6 -6
  21. data/lib/familia/horreum/management.rb +29 -0
  22. data/lib/familia/version.rb +1 -1
  23. data/try/features/relationships/prefix_vs_config_name_try.rb +418 -0
  24. metadata +3 -27
  25. data/docs/migrating/v2.0.0-pre.md +0 -84
  26. data/docs/migrating/v2.0.0-pre11.md +0 -253
  27. data/docs/migrating/v2.0.0-pre12.md +0 -306
  28. data/docs/migrating/v2.0.0-pre13.md +0 -95
  29. data/docs/migrating/v2.0.0-pre14.md +0 -37
  30. data/docs/migrating/v2.0.0-pre18.md +0 -58
  31. data/docs/migrating/v2.0.0-pre19.md +0 -197
  32. data/docs/migrating/v2.0.0-pre22.md +0 -241
  33. data/docs/migrating/v2.0.0-pre5.md +0 -131
  34. data/docs/migrating/v2.0.0-pre6.md +0 -154
  35. data/docs/migrating/v2.0.0-pre7.md +0 -222
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: familia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre25
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -9,20 +9,6 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: benchmark
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - "~>"
17
- - !ruby/object:Gem::Version
18
- version: '0.4'
19
- type: :runtime
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - "~>"
24
- - !ruby/object:Gem::Version
25
- version: '0.4'
26
12
  - !ruby/object:Gem::Dependency
27
13
  name: concurrent-ruby
28
14
  requirement: !ruby/object:Gem::Requirement
@@ -194,17 +180,6 @@ files:
194
180
  - docs/guides/thread-safety-monitoring.md
195
181
  - docs/guides/time-literals.md
196
182
  - docs/migrating/.gitignore
197
- - docs/migrating/v2.0.0-pre.md
198
- - docs/migrating/v2.0.0-pre11.md
199
- - docs/migrating/v2.0.0-pre12.md
200
- - docs/migrating/v2.0.0-pre13.md
201
- - docs/migrating/v2.0.0-pre14.md
202
- - docs/migrating/v2.0.0-pre18.md
203
- - docs/migrating/v2.0.0-pre19.md
204
- - docs/migrating/v2.0.0-pre22.md
205
- - docs/migrating/v2.0.0-pre5.md
206
- - docs/migrating/v2.0.0-pre6.md
207
- - docs/migrating/v2.0.0-pre7.md
208
183
  - docs/overview.md
209
184
  - docs/qodo-merge-compliance.md
210
185
  - docs/reference/api-technical.md
@@ -377,6 +352,7 @@ files:
377
352
  - try/features/relationships/participation_target_class_resolution_try.rb
378
353
  - try/features/relationships/participation_through_try.rb
379
354
  - try/features/relationships/participation_unresolved_target_try.rb
355
+ - try/features/relationships/prefix_vs_config_name_try.rb
380
356
  - try/features/relationships/relationships_api_changes_try.rb
381
357
  - try/features/relationships/relationships_edge_cases_try.rb
382
358
  - try/features/relationships/relationships_performance_minimal_try.rb
@@ -556,7 +532,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
556
532
  requirements:
557
533
  - - ">="
558
534
  - !ruby/object:Gem::Version
559
- version: 3.3.6
535
+ version: '3.2'
560
536
  required_rubygems_version: !ruby/object:Gem::Requirement
561
537
  requirements:
562
538
  - - ">="
@@ -1,84 +0,0 @@
1
- # Migrating Guide: v1.x to v2.0.0-pre
2
-
3
- This guide covers migrating from Familia v1.x to the v2.0.0-pre foundation release.
4
-
5
- ## Overview
6
-
7
- The v2.0.0-pre series represents a major modernization of Familia with:
8
- - Complete API redesign for clarity and consistency
9
- - Valkey compatibility alongside Valkey/Redis support
10
- - Ruby 3.4+ modernization with improved thread safety
11
- - New connection pooling architecture
12
-
13
- ## Step-by-Step Migration
14
-
15
- ### 1. Update Connection Configuration
16
-
17
- **Before (v1.x):**
18
- ```ruby
19
- Familia.connect('redis://localhost:6379/0')
20
- ```
21
-
22
- **After (v2.0.0-pre):**
23
- ```ruby
24
- Familia.configure do |config|
25
- config.redis_uri = 'redis://localhost:6379/0'
26
- config.connection_pool = true
27
- end
28
- ```
29
-
30
- ### 2. Migrate Identifier Declarations
31
-
32
- **Before (v1.x):**
33
- ```ruby
34
- class User < Familia::Base
35
- identifier :user_id
36
- end
37
- ```
38
-
39
- **After (v2.0.0-pre):**
40
- ```ruby
41
- class User < Familia::Horreum
42
- identifier_field :user_id
43
- end
44
- ```
45
-
46
- ### 3. Update Feature Activations
47
-
48
- **Before (v1.x):**
49
- ```ruby
50
- class User < Familia::Base
51
- include Familia::Features::Expiration
52
- end
53
- ```
54
-
55
- **After (v2.0.0-pre):**
56
- ```ruby
57
- class User < Familia::Horreum
58
- feature :expiration
59
- end
60
- ```
61
-
62
- ### 4. Review Method Calls
63
-
64
- Several methods were renamed for consistency:
65
-
66
- | v1.x Method | v2.0.0-pre Method | Notes |
67
- |-------------|------------------|-------|
68
- | `delete` | `destroy` | More semantic naming |
69
- | `exists` | `exists?` | Ruby predicate convention |
70
- | `dump` | `serialize` | Clearer intent |
71
-
72
- ## Breaking Changes
73
-
74
- - `Familia::Base` replaced by `Familia::Horreum`
75
- - Connection configuration moved to block-based setup
76
- - Feature inclusion changed from `include` to `feature` declarations
77
- - Several method names updated for consistency
78
-
79
- ## Next Steps
80
-
81
- After completing the foundation migration:
82
- 1. Review [Security Feature Adoption](v2.0.0-pre5.md) for encrypted fields
83
- 2. See [Architecture Migration](v2.0.0-pre6.md) for persistence improvements
84
- 3. Explore [Relationships Migration](v2.0.0-pre7.md) for the new relationship system
@@ -1,253 +0,0 @@
1
- # Migrating Guide: v2.0.0-pre11
2
-
3
- This version introduces significant improvements to Familia's feature system, making it easier to organize and use features across complex projects.
4
-
5
- ## Enhanced Feature System
6
-
7
- ### Model-Specific Feature Registration
8
-
9
- Previously, all features were registered globally. Now you can register features specific to individual model classes, allowing for better organization and namespace management.
10
-
11
- #### Before
12
- ```ruby
13
- # Global feature registration only
14
- module MyProjectFeature
15
- # Feature implementation
16
- end
17
- Familia::Base.add_feature MyProjectFeature, :my_project_feature
18
-
19
- class Customer < Familia::Horreum
20
- feature :my_project_feature
21
- end
22
-
23
- class Session < Familia::Horreum
24
- feature :my_project_feature # Same global feature
25
- end
26
- ```
27
-
28
- #### After
29
- ```ruby
30
- # Model-specific feature registration
31
- module CustomerSpecificFeature
32
- # Feature implementation
33
- end
34
-
35
- # Register feature only for Customer and its subclasses
36
- Customer.add_feature CustomerSpecificFeature, :customer_specific
37
-
38
- class Customer < Familia::Horreum
39
- feature :customer_specific # Available via Customer's registry
40
- end
41
-
42
- class PremiumCustomer < Customer
43
- feature :customer_specific # Inherited via ancestry chain
44
- end
45
-
46
- class Session < Familia::Horreum
47
- # feature :customer_specific # Not available - would raise error
48
- end
49
- ```
50
-
51
- **Benefits:**
52
- - Features can have the same name across different model hierarchies
53
- - Standardized naming: `deprecated_fields.rb` instead of `customer_deprecated_fields.rb`
54
- - Natural inheritance through Ruby's class hierarchy
55
-
56
- ## SafeDump DSL Improvements
57
-
58
- The new DSL replaces the brittle `@safe_dump_fields` class instance variable pattern with clean, explicit methods.
59
-
60
- ### Before
61
- ```ruby
62
- class Customer < Familia::Horreum
63
- feature :safe_dump
64
-
65
- # Brittle - hard to move to feature modules, confusing syntax
66
- @safe_dump_fields = [
67
- :custid,
68
- :email,
69
- { active: ->(obj) { obj.active? } },
70
- { display_name: ->(obj) { "#{obj.name} (#{obj.custid})" } }
71
- ]
72
- end
73
- ```
74
-
75
- ### After
76
- ```ruby
77
- class Customer < Familia::Horreum
78
- feature :safe_dump
79
-
80
- # Clean DSL - easy to understand and organize
81
- safe_dump_field :custid
82
- safe_dump_field :email
83
- safe_dump_field :active, ->(obj) { obj.active? }
84
- safe_dump_field :display_name, ->(obj) { "#{obj.name} (#{obj.custid})" }
85
-
86
- # Or define multiple fields at once
87
- safe_dump_fields :created, :updated, { status: ->(obj) { obj.role } }
88
- end
89
- ```
90
-
91
- **New methods available:**
92
- - `safe_dump_field(name, callable = nil)` - Define a single field
93
- - `safe_dump_fields(*fields)` - Define multiple fields or get field names
94
- - `safe_dump_field_names` - Get array of field names
95
- - `safe_dump_field_map` - Get the internal callable map
96
-
97
- **Backward Compatibility:**
98
- - `set_safe_dump_fields(*fields)` - Legacy setter method (still works)
99
- - The old `@safe_dump_fields` pattern is no longer supported
100
-
101
- ## Auto-loading Features
102
-
103
- ### Before: Manual Loading
104
- ```ruby
105
- # customer/features.rb
106
-
107
- # Manual feature loading (copied from Familia)
108
- features_dir = File.join(__dir__, 'features')
109
- if Dir.exist?(features_dir)
110
- Dir.glob(File.join(features_dir, '*.rb')).each do |feature_file|
111
- require_relative feature_file
112
- end
113
- end
114
-
115
- class Customer < Familia::Horreum
116
- # Features now available for use
117
- feature :deprecated_fields
118
- end
119
- ```
120
-
121
- ### After: Automatic Loading
122
- ```ruby
123
- # customer/features.rb
124
-
125
- class Customer < Familia::Horreum
126
- module Features
127
- include Familia::Features::Autoloader
128
- # Automatically discovers and loads all *.rb files from customer/features/
129
- end
130
- end
131
-
132
- class Customer < Familia::Horreum
133
- # Features automatically loaded and available
134
- feature :deprecated_fields
135
- end
136
- ```
137
-
138
- **Directory structure this enables:**
139
- ```
140
- models/
141
- ├── customer/
142
- │ ├── features/
143
- │ │ ├── deprecated_fields.rb # Standardized names!
144
- │ │ ├── legacy_support.rb
145
- │ │ └── stripe_integration.rb
146
- │ └── features.rb # Include Autoloader here
147
- ├── session/
148
- │ ├── features/
149
- │ │ ├── deprecated_fields.rb # Same name, different implementation
150
- │ │ └── expiration_hooks.rb
151
- │ └── features.rb
152
- └── customer.rb
153
- ```
154
-
155
- ## Field Definitions in Feature Modules
156
-
157
- Feature modules can now define fields directly in their `ClassMethods` modules. When a class extends the module, the field definitions execute in the extending class's context.
158
-
159
- ### Example
160
- ```ruby
161
- # features/common_fields.rb
162
-
163
- module CommonFields
164
- def self.included(base)
165
- base.extend ClassMethods
166
- end
167
-
168
- module ClassMethods
169
- # These field calls execute in the extending class's context
170
- field :created
171
- field :updated
172
- field :version
173
-
174
- def touch_updated
175
- self.updated = Familia.now.to_i
176
- end
177
- end
178
-
179
- Familia::Base.add_feature self, :common_fields
180
- end
181
-
182
- # Usage
183
- class Customer < Familia::Horreum
184
- feature :common_fields
185
- # Now has :created, :updated, :version fields and touch_updated class method
186
- end
187
- ```
188
-
189
- ## Migration Steps
190
-
191
- ### 1. Update SafeDump Usage
192
- Replace all `@safe_dump_fields` definitions with the new DSL:
193
-
194
- ```ruby
195
- # Find and replace pattern:
196
- # Old: @safe_dump_fields = [:field1, :field2, { field3: ->(obj) { ... } }]
197
- # New: safe_dump_fields :field1, :field2, { field3: ->(obj) { ... } }
198
-
199
- # Or use individual field definitions for better readability:
200
- safe_dump_field :field1
201
- safe_dump_field :field2
202
- safe_dump_field :field3, ->(obj) { ... }
203
- ```
204
-
205
- ### 2. UnsortedSet Up Auto-loading (Optional)
206
- If you have project-specific features, set up auto-loading:
207
-
208
- ```ruby
209
- # Create: models/[model_name]/features.rb
210
- module YourProject
211
- class ModelName < Familia::Horreum
212
- module Features
213
- include Familia::Features::Autoloader
214
- end
215
- end
216
- end
217
-
218
- # Require this file before your model definitions
219
- require_relative 'model_name/features'
220
- ```
221
-
222
- ### 3. Organize Features by Model (Optional)
223
- Consider reorganizing shared feature names by model:
224
-
225
- ```ruby
226
- # Before: features/customer_deprecated_fields.rb
227
- # After: models/customer/features/deprecated_fields.rb
228
-
229
- # This allows multiple models to have their own deprecated_fields.rb
230
- ```
231
-
232
- ### 4. Test Your Changes
233
- Run your test suite to ensure all SafeDump functionality works correctly:
234
-
235
- ```ruby
236
- # Verify SafeDump DSL works
237
- model = YourModel.new(field1: 'value')
238
- result = model.safe_dump
239
- puts result.keys # Should include your defined fields
240
- ```
241
-
242
- ## Breaking Changes
243
-
244
- 1. **`@safe_dump_fields` no longer supported** - Must migrate to DSL methods
245
- 2. **SafeDump field order** - Fields are now returned in definition order via Hash keys (Ruby 1.9+ behavior)
246
-
247
- ## New Capabilities Unlocked
248
-
249
- 1. **Standardized feature names** across different models
250
- 2. **Cleaner SafeDump definitions** that can be easily moved to feature modules
251
- 3. **Automatic feature discovery** for better project organization
252
- 4. **Model-specific feature registries** for better namespace management
253
- 5. **Field definitions in feature modules** for shared functionality
@@ -1,306 +0,0 @@
1
- # Migrating Guide: v2.0.0-pre12
2
-
3
- This version introduces significant security improvements to Familia's identifier system, including verifiable identifiers with HMAC signatures, scoped identifier namespaces, and hardened external identifier derivation to prevent potential security vulnerabilities.
4
-
5
- ## VerifiableIdentifier Feature
6
-
7
- ### Overview
8
-
9
- The new `Familia::VerifiableIdentifier` module allows applications to create and verify identifiers with embedded HMAC signatures. This enables stateless confirmation that an identifier was generated by your application, preventing forged IDs from malicious sources.
10
-
11
- ### Basic Usage
12
-
13
- ```ruby
14
- class Customer < Familia::Horreum
15
- feature :verifiable_identifier
16
-
17
- # Required: UnsortedSet the HMAC secret (do this once in your app initialization)
18
- # Generate with: SecureRandom.hex(64)
19
- ENV['VERIFIABLE_ID_HMAC_SECRET'] = 'your_64_character_hex_secret'
20
- end
21
-
22
- # Generate a verifiable identifier
23
- customer = Customer.new
24
- verifiable_id = customer.generate_verifiable_id
25
- # => "cust_1234567890abcdef_a1b2c3d4e5f6789..."
26
-
27
- # Verify the identifier later (stateless verification)
28
- if Customer.verified_identifier?(verifiable_id)
29
- # Identifier is valid and was generated by this application
30
- original_id = Customer.extract_identifier(verifiable_id)
31
- customer = Customer.new(original_id)
32
- else
33
- # Identifier is forged or corrupted
34
- raise SecurityError, "Invalid identifier"
35
- end
36
- ```
37
-
38
- ### Scoped VerifiableIdentifier
39
-
40
- The new `scope` parameter enables cryptographically isolated identifier namespaces for multi-tenant, multi-domain, or multi-environment applications.
41
-
42
- #### Before (Global Scope)
43
- ```ruby
44
- # All identifiers share the same cryptographic space
45
- admin_id = admin.generate_verifiable_id
46
- user_id = user.generate_verifiable_id
47
-
48
- # Risk: Cross-contamination between different contexts
49
- ```
50
-
51
- #### After (Scoped Namespaces)
52
- ```ruby
53
- # Production environment
54
- prod_customer_id = customer.generate_verifiable_id(scope: 'production')
55
- prod_admin_id = admin.generate_verifiable_id(scope: 'production:admin')
56
-
57
- # Development environment
58
- dev_customer_id = customer.generate_verifiable_id(scope: 'development')
59
-
60
- # Multi-tenant application
61
- tenant_a_id = user.generate_verifiable_id(scope: "tenant:#{tenant_a.id}")
62
- tenant_b_id = user.generate_verifiable_id(scope: "tenant:#{tenant_b.id}")
63
-
64
- # Verification requires matching scope
65
- Customer.verified_identifier?(prod_customer_id, scope: 'production') # => true
66
- Customer.verified_identifier?(prod_customer_id, scope: 'development') # => false
67
- ```
68
-
69
- **Scope Benefits:**
70
- - **Multi-tenant isolation**: Tenant A cannot forge identifiers for Tenant B
71
- - **Environment separation**: Production IDs cannot be used in development
72
- - **Role-based security**: Admin scopes separate from user scopes
73
- - **Full backward compatibility**: Existing code without scopes continues to work
74
-
75
- ### Key Management
76
-
77
- #### Secure Secret Generation
78
- ```ruby
79
- # Generate a cryptographically secure HMAC secret
80
- require 'securerandom'
81
- secret = SecureRandom.hex(64) # 512-bit secret
82
- puts "VERIFIABLE_ID_HMAC_SECRET=#{secret}"
83
- ```
84
-
85
- #### Environment Configuration
86
- ```ruby
87
- # config/application.rb or equivalent
88
- # UnsortedSet this BEFORE any VerifiableIdentifier usage
89
- ENV['VERIFIABLE_ID_HMAC_SECRET'] = Rails.application.credentials.verifiable_id_secret
90
-
91
- # Or configure programmatically
92
- Familia::VerifiableIdentifier.hmac_secret = your_secret_string
93
- ```
94
-
95
- ## ObjectIdentifier Feature Improvements
96
-
97
- ### Method Renaming
98
-
99
- Method names have been updated for clarity and consistency:
100
-
101
- #### Before
102
- ```ruby
103
- customer = Customer.new
104
- objid = customer.generate_objid # Unclear what this generates
105
- extid = Customer.generate_extid(objid) # Less secure class method
106
- ```
107
-
108
- #### After
109
- ```ruby
110
- customer = Customer.new
111
- objid = customer.generate_object_identifier # Clear: generates object ID
112
- extid = customer.derive_external_identifier # Clear: derives from objid, instance method
113
- ```
114
-
115
- **Migration:**
116
- - Replace `generate_objid` → `generate_object_identifier`
117
- - Replace `generate_external_identifier` → `derive_external_identifier`
118
- - Remove usage of `generate_extid` (deprecated for security reasons)
119
-
120
- ### Provenance Tracking
121
-
122
- ObjectIdentifier now tracks which generator was used for each identifier:
123
-
124
- ```ruby
125
- class Customer < Familia::Horreum
126
- feature :object_identifier
127
-
128
- # Configure generator type
129
- object_identifier_generator :uuid_v7 # or :uuid_v4, :hex, custom proc
130
- end
131
-
132
- customer = Customer.new
133
- objid = customer.generate_object_identifier
134
-
135
- # Provenance information available
136
- puts customer.object_identifier_generator_type # => :uuid_v7
137
- puts customer.objid_format # => :uuid (normalized format)
138
- ```
139
-
140
- **Benefits:**
141
- - **Security auditing**: Know which generator created each identifier
142
- - **Format normalization**: Eliminates ambiguity between UUID and hex formats
143
- - **Migration support**: Track mixed generator usage during transitions
144
-
145
- ## ExternalIdentifier Security Hardening
146
-
147
- ### Provenance Validation
148
-
149
- ExternalIdentifier now validates that objid values come from the ObjectIdentifier feature before deriving external identifiers.
150
-
151
- #### Before (Potential Security Risk)
152
- ```ruby
153
- # Could derive external IDs from any string, including malicious input
154
- extid = customer.derive_external_identifier("malicious_input")
155
- ```
156
-
157
- #### After (Hardened)
158
- ```ruby
159
- customer = Customer.new
160
- customer.generate_object_identifier # Must generate objid first
161
-
162
- # Only works with validated objid from ObjectIdentifier feature
163
- extid = customer.derive_external_identifier # Secure: uses validated objid
164
- ```
165
-
166
- ### Improved Security Model
167
-
168
- External identifiers are now derived using the internal objid as a seed for a new random value, rather than directly deriving from objid.
169
-
170
- #### Before
171
- ```ruby
172
- # Direct derivation could leak information about objid
173
- extid = hash(objid) # Information leakage risk
174
- ```
175
-
176
- #### After
177
- ```ruby
178
- # objid used as seed for new random value
179
- extid = secure_hash(objid + additional_entropy) # No information leakage
180
- ```
181
-
182
- ### Error Handling Improvements
183
-
184
- External identifier now raises clear errors for invalid usage:
185
-
186
- ```ruby
187
- class Customer < Familia::Horreum
188
- feature :external_identifier # Missing: object_identifier dependency
189
- end
190
-
191
- customer = Customer.new
192
- # Raises ExternalIdentifierError instead of returning nil
193
- customer.derive_external_identifier
194
- # => Familia::ExternalIdentifierError: Model does not have an objid field
195
- ```
196
-
197
- ## Migration Steps
198
-
199
- ### 1. Update Method Names
200
-
201
- Replace deprecated method names in your codebase:
202
-
203
- ```bash
204
- # Search and replace patterns:
205
- grep -r "generate_objid" --include="*.rb" .
206
- # Replace with: generate_object_identifier
207
-
208
- grep -r "generate_external_identifier" --include="*.rb" .
209
- # Replace with: derive_external_identifier
210
-
211
- grep -r "generate_extid" --include="*.rb" .
212
- # Remove usage - use derive_external_identifier instead
213
- ```
214
-
215
- ### 2. Add HMAC Secret for VerifiableIdentifier
216
-
217
- If you plan to use VerifiableIdentifier:
218
-
219
- ```ruby
220
- # Generate secret
221
- require 'securerandom'
222
- secret = SecureRandom.hex(64)
223
-
224
- # Add to your environment configuration
225
- # .env, Rails credentials, or similar
226
- VERIFIABLE_ID_HMAC_SECRET=your_generated_secret
227
-
228
- # Verify configuration
229
- puts ENV['VERIFIABLE_ID_HMAC_SECRET']&.length # Should be 128 characters
230
- ```
231
-
232
- ### 3. Update ExternalIdentifier Usage
233
-
234
- Ensure proper dependency chain:
235
-
236
- ```ruby
237
- class YourModel < Familia::Horreum
238
- # Required: ObjectIdentifier must come before ExternalIdentifier
239
- feature :object_identifier
240
- feature :external_identifier
241
-
242
- # Configure generator if needed
243
- object_identifier_generator :uuid_v7
244
- end
245
-
246
- # Usage pattern
247
- model = YourModel.new
248
- model.generate_object_identifier # Generate objid first
249
- extid = model.derive_external_identifier # Then derive external ID
250
- ```
251
-
252
- ### 4. Review Security-Sensitive Code
253
-
254
- Audit any code that processes identifiers from external sources:
255
-
256
- ```ruby
257
- # Before: Potentially unsafe
258
- def process_identifier(external_id)
259
- # Could process forged identifiers
260
- model = Model.find_by_external_id(external_id)
261
- end
262
-
263
- # After: With verification
264
- def process_identifier(verifiable_id)
265
- # Verify identifier authenticity first
266
- unless Model.verified_identifier?(verifiable_id)
267
- raise SecurityError, "Invalid identifier"
268
- end
269
-
270
- original_id = Model.extract_identifier(verifiable_id)
271
- model = Model.new(original_id)
272
- end
273
- ```
274
-
275
- ## Breaking Changes
276
-
277
- 1. **`generate_extid` removed** - Use instance-level `derive_external_identifier` instead
278
- 2. **ExternalIdentifier validation** - Now raises `ExternalIdentifierError` instead of returning `nil` for models without objid
279
- 3. **Method names changed** - `generate_objid` → `generate_object_identifier`, `generate_external_identifier` → `derive_external_identifier`
280
-
281
- ## New Security Capabilities
282
-
283
- 1. **Cryptographic identifier verification** - Prevent forged IDs with HMAC signatures
284
- 2. **Scoped namespaces** - Isolate identifiers by tenant, environment, or role
285
- 3. **Provenance tracking** - Know which generator created each identifier
286
- 4. **Information leakage prevention** - External IDs no longer directly expose internal IDs
287
- 5. **Input validation** - Clear error messages for invalid operations
288
-
289
- ## Testing Your Migration
290
-
291
- ```ruby
292
- # Test ObjectIdentifier changes
293
- model = YourModel.new
294
- objid = model.generate_object_identifier
295
- extid = model.derive_external_identifier
296
- puts "Generator: #{model.object_identifier_generator_type}"
297
-
298
- # Test VerifiableIdentifier (if using)
299
- vid = model.generate_verifiable_id
300
- puts "Verifiable: #{YourModel.verified_identifier?(vid)}"
301
-
302
- # Test scoped identifiers (if using)
303
- scoped_vid = model.generate_verifiable_id(scope: 'production')
304
- puts "Scoped valid: #{YourModel.verified_identifier?(scoped_vid, scope: 'production')}"
305
- puts "Wrong scope: #{YourModel.verified_identifier?(scoped_vid, scope: 'development')}"
306
- ```