Complete step-by-step guide for mounting BreakEscape engine in Hacktivity: - Gemfile and bundle installation - Route mounting at /break_escape - Database migration installation - User model compatibility verification - Static asset configuration - Session and CSRF setup - Content Security Policy (CSP) configuration - Testing integration - Deployment to staging - Troubleshooting guide - Verification checklist - Performance monitoring - Rollback plan This completes the full documentation set (7 files, ~140KB total)
24 KiB
Hacktivity Integration Guide
Complete guide for integrating the BreakEscape Rails Engine into the Hacktivity platform.
Target: Hacktivity Rails application
Engine: BreakEscape (mountable, isolated namespace)
Mount Point: /break_escape
Prerequisites: Completed Phases 1-12 of implementation plan
Overview
This guide walks through mounting the BreakEscape engine into Hacktivity, ensuring:
- ✅ Engine mounts at
/break_escape - ✅ Uses Hacktivity's User model for authentication
- ✅ Shares Hacktivity's session and CSRF protection
- ✅ Database migrations install correctly
- ✅ Static assets serve from
public/break_escape/ - ✅ Authorization integrates with Hacktivity's policies
- ✅ Tests run in Hacktivity's test suite
Phase 1: Add BreakEscape to Gemfile
Step 1.1: Update Hacktivity's Gemfile
Location: /path/to/hacktivity/Gemfile
Add the BreakEscape engine gem:
# Gaming engines
gem 'break_escape', path: '../BreakEscape'
Note: Adjust the path: to point to your BreakEscape directory. Use relative or absolute paths.
Step 1.2: Install the gem
cd /path/to/hacktivity
bundle install
Expected output:
Fetching gem metadata from https://rubygems.org/...
Using break_escape 0.1.0 from source at `../BreakEscape`
Bundle complete!
Step 1.3: Verify installation
bundle show break_escape
Expected output:
/path/to/BreakEscape
Phase 2: Mount Engine Routes
Step 2.1: Update Hacktivity's routes.rb
Location: /path/to/hacktivity/config/routes.rb
Add the engine mount point:
Rails.application.routes.draw do
# Existing Hacktivity routes
devise_for :users
# ... other routes ...
# BreakEscape game engine
mount BreakEscape::Engine, at: '/break_escape'
# ... remaining routes ...
end
Step 2.2: Verify routes
rails routes | grep break_escape
Expected output:
break_escape /break_escape BreakEscape::Engine
Routes for BreakEscape::Engine:
missions GET /missions(.:format) break_escape/missions#index
mission GET /missions/:id(.:format) break_escape/missions#show
games POST /games(.:format) break_escape/games#create
game GET /games/:id(.:format) break_escape/games#show
game_scenario GET /games/:id/scenario(.:format) break_escape/games#scenario
game_ink GET /games/:id/ink(.:format) break_escape/games#ink
game_bootstrap GET /games/:id/bootstrap(.:format) break_escape/api/games#bootstrap
game_sync_state PUT /games/:id/sync_state(.:format) break_escape/api/games#sync_state
game_unlock POST /games/:id/unlock(.:format) break_escape/api/games#unlock
game_inventory POST /games/:id/inventory(.:format) break_escape/api/games#inventory
root GET / break_escape/missions#index
Phase 3: Install Database Migrations
Step 3.1: Copy migrations from engine
cd /path/to/hacktivity
rails break_escape:install:migrations
Expected output:
Copied migration 20251120120001_create_break_escape_missions.break_escape.rb from break_escape
Copied migration 20251120120002_create_break_escape_games.break_escape.rb from break_escape
Copied migration 20251120120003_create_break_escape_demo_users.break_escape.rb from break_escape
Step 3.2: Review migrations
ls -la db/migrate/ | grep break_escape
You should see:
*_create_break_escape_missions.break_escape.rb*_create_break_escape_games.break_escape.rb*_create_break_escape_demo_users.break_escape.rb(optional, for standalone mode)
Step 3.3: Run migrations
rails db:migrate
Expected output:
== CreateBreakEscapeMissions: migrating =====================================
-- create_table(:break_escape_missions)
-> 0.0234s
-- add_index(:break_escape_missions, :name, {:unique=>true})
-> 0.0089s
-- add_index(:break_escape_missions, :published)
-> 0.0067s
== CreateBreakEscapeMissions: migrated (0.0392s) ============================
== CreateBreakEscapeGames: migrating ========================================
-- create_table(:break_escape_games)
-> 0.0456s
-- add_index(:break_escape_games, [:player_type, :player_id, :mission_id], {:unique=>true, :name=>"index_games_on_player_and_mission"})
-> 0.0123s
-- add_index(:break_escape_games, :scenario_data, {:using=>:gin})
-> 0.0234s
-- add_index(:break_escape_games, :player_state, {:using=>:gin})
-> 0.0198s
-- add_index(:break_escape_games, :status)
-> 0.0078s
== CreateBreakEscapeGames: migrated (0.1091s) ===============================
Step 3.4: Verify tables
rails db:migrate:status
Look for:
up 20251120120001 Create break escape missions
up 20251120120002 Create break escape games
Or check in PostgreSQL:
psql -d hacktivity_development -c "\dt break_escape_*"
Expected output:
List of relations
Schema | Name | Type | Owner
--------+-------------------------+-------+----------
public | break_escape_games | table | postgres
public | break_escape_missions | table | postgres
Phase 4: Verify User Model Compatibility
Step 4.1: Check User model
Location: /path/to/hacktivity/app/models/user.rb
Ensure the User model exists and uses Devise:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
# BreakEscape will use polymorphic association
has_many :break_escape_games,
class_name: 'BreakEscape::Game',
as: :player,
dependent: :destroy
end
Note: The has_many :break_escape_games line is optional but recommended for convenience.
Step 4.2: Test polymorphic association
rails console
# Create a test user
user = User.create!(email: 'test@example.com', password: 'password123')
# Verify BreakEscape can find/use this user
BreakEscape::Game.create!(
player: user,
mission: BreakEscape::Mission.first,
scenario_data: {startRoom: 'test'},
player_state: {currentRoom: nil}
)
# Should work without errors
Phase 5: Seed Mission Data
Step 5.1: Run BreakEscape seeds
If you have seed data in db/seeds/break_escape_missions.rb:
rails db:seed
Or manually create missions:
rails console
BreakEscape::Mission.create!([
{
name: 'ceo_exfil',
display_name: 'CEO Exfiltration',
description: 'Infiltrate the corporate office and gather evidence of insider trading.',
published: true,
difficulty_level: 3
},
{
name: 'cybok_heist',
display_name: 'CybOK Heist',
description: 'Break into the research facility and steal the CybOK framework.',
published: true,
difficulty_level: 4
}
])
Step 5.2: Verify missions
rails console
BreakEscape::Mission.count # Should be > 0
BreakEscape::Mission.published.pluck(:display_name)
# => ["CEO Exfiltration", "CybOK Heist"]
Phase 6: Configure Static Assets
Step 6.1: Verify public/ directory
Ensure BreakEscape's static assets are in the engine's public/ directory:
ls -la /path/to/BreakEscape/public/break_escape/
Expected structure:
public/break_escape/
├── js/
│ ├── phaser.min.js
│ ├── game.js
│ ├── scenes/
│ └── utils/
├── css/
│ └── game.css
└── assets/
├── images/
└── audio/
Step 6.2: Test asset serving
Start the Rails server:
cd /path/to/hacktivity
rails server
Visit in browser:
http://localhost:3000/break_escape/js/phaser.min.js
http://localhost:3000/break_escape/css/game.css
Expected: Files should load correctly (200 status).
Note: Rails engines automatically serve files from public/ at the mount path.
Phase 7: Test the Integration
Step 7.1: Start Hacktivity server
cd /path/to/hacktivity
rails server
Step 7.2: Test mission listing
Visit: http://localhost:3000/break_escape
Expected: You should see the missions index page listing all published missions.
Step 7.3: Test game creation (as logged-in user)
- Log in to Hacktivity as a user
- Visit:
http://localhost:3000/break_escape/missions/1 - Click: "Start Mission" or similar
- Expected: Redirects to
/break_escape/games/:id
Step 7.4: Test API endpoints
Open browser console and run:
// Get CSRF token
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
// Get current game ID from URL
const gameId = window.location.pathname.match(/games\/(\d+)/)[1];
// Test bootstrap endpoint
fetch(`/break_escape/games/${gameId}/bootstrap`, {
method: 'GET',
credentials: 'same-origin',
headers: {
'Accept': 'application/json'
}
})
.then(r => r.json())
.then(data => console.log('Bootstrap:', data));
// Test scenario endpoint
fetch(`/break_escape/games/${gameId}/scenario`, {
method: 'GET',
credentials: 'same-origin',
headers: {
'Accept': 'application/json'
}
})
.then(r => r.json())
.then(data => console.log('Scenario:', data));
Expected: Both should return JSON with game data.
Step 7.5: Test unlock endpoint
fetch(`/break_escape/games/${gameId}/unlock`, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({
targetType: 'door',
targetId: 'office',
attempt: 'test_password',
method: 'password'
})
})
.then(r => r.json())
.then(data => console.log('Unlock result:', data));
Expected: Returns {success: true/false, ...}
Phase 8: Configure Authorization (Optional)
If using Pundit in Hacktivity, ensure policies work correctly.
Step 8.1: Verify Pundit is installed
Location: /path/to/hacktivity/Gemfile
gem 'pundit'
Step 8.2: Check BreakEscape policies
Location: /path/to/BreakEscape/app/policies/break_escape/
Ensure these exist:
game_policy.rbmission_policy.rb
Step 8.3: Test policy enforcement
rails console
user = User.first
game = BreakEscape::Game.create!(player: user, mission: BreakEscape::Mission.first)
# Test policy
policy = BreakEscape::GamePolicy.new(user, game)
policy.show? # Should be true (user owns game)
# Test with different user
other_user = User.create!(email: 'other@example.com', password: 'password')
policy = BreakEscape::GamePolicy.new(other_user, game)
policy.show? # Should be false (doesn't own game)
Phase 9: Run Tests in Hacktivity
Step 9.1: Add BreakEscape test helpers to Hacktivity
Location: /path/to/hacktivity/test/test_helper.rb
Add after existing setup:
# Include BreakEscape test helpers
require 'break_escape/test_helper' if defined?(BreakEscape)
Step 9.2: Run BreakEscape tests
cd /path/to/hacktivity
rails test
This runs all tests including BreakEscape's.
To run only BreakEscape tests:
rails test test/models/break_escape/**/*_test.rb
rails test test/controllers/break_escape/**/*_test.rb
Step 9.3: Verify test fixtures work
Check that Hacktivity's User fixtures are accessible:
rails console -e test
# Should find test users
User.find_by(email: 'test@example.com')
# BreakEscape should be able to create games
game = BreakEscape::Game.create!(
player: User.first,
mission: BreakEscape::Mission.first
)
Phase 10: Configure Session & CSRF
Step 10.1: Verify session sharing
BreakEscape should automatically share Hacktivity's session. Test:
rails console
# Check session store
Rails.application.config.session_store
# => :cookie_store or :active_record_store
Note: BreakEscape uses credentials: 'same-origin' in API calls to share cookies.
Step 10.2: Verify CSRF protection
Check that Hacktivity includes CSRF meta tags:
Location: /path/to/hacktivity/app/views/layouts/application.html.erb
Should include:
<%= csrf_meta_tags %>
Test in BreakEscape views:
Location: /path/to/BreakEscape/app/views/layouts/break_escape/application.html.erb
Should include:
<!DOCTYPE html>
<html>
<head>
<meta name="csrf-token" content="<%= form_authenticity_token %>">
<%= javascript_tag nonce: true do %>
window.CSRF_TOKEN = '<%= form_authenticity_token %>';
<% end %>
</head>
<body>
<%= yield %>
</body>
</html>
Step 10.3: Test CSRF in API calls
From browser console:
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
console.log('CSRF Token:', csrfToken); // Should be a long string
Make an API call:
fetch('/break_escape/games/1/sync_state', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({currentRoom: 'test'})
});
Expected: 200 status, not 422 (CSRF verification failed).
Phase 11: Configure Content Security Policy (CSP)
Step 11.1: Check Hacktivity's CSP configuration
Location: /path/to/hacktivity/config/initializers/content_security_policy.rb
If CSP is enabled:
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
policy.script_src :self, :https
# ... other directives ...
end
Step 11.2: Enable nonces for BreakEscape
Update CSP to allow inline scripts with nonces:
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
policy.script_src :self, :https, :unsafe_inline if Rails.env.development?
policy.script_src :self, :https
# Allow nonces for inline scripts
policy.script_src_elem :self, :https
end
Rails.application.config.content_security_policy_nonce_generator = ->(request) {
SecureRandom.base64(16)
}
Rails.application.config.content_security_policy_nonce_directives = %w[script-src]
Step 11.3: Verify nonces in BreakEscape views
Check that BreakEscape views use nonces:
<%= javascript_tag nonce: true do %>
window.GAME_ID = <%= @game.id %>;
<% end %>
Phase 12: Deploy to Staging
Step 12.1: Update production config
Location: /path/to/hacktivity/config/environments/production.rb
Ensure static assets are served:
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
Or use a CDN/nginx to serve public/break_escape/ assets.
Step 12.2: Precompile assets (if using asset pipeline)
RAILS_ENV=production rails assets:precompile
Note: BreakEscape uses static assets in public/, so this is optional.
Step 12.3: Run migrations on staging
RAILS_ENV=staging rails db:migrate
Step 12.4: Seed missions on staging
RAILS_ENV=staging rails db:seed
Step 12.5: Test on staging
Visit staging URL:
https://staging.hacktivity.com/break_escape
Test:
- Mission listing loads
- User can start a game
- API endpoints work
- Ink scripts load correctly
- Game state persists across page reloads
Troubleshooting
Problem: "Uninitialized constant BreakEscape"
Solution:
- Verify
gem 'break_escape'is in Gemfile - Run
bundle install - Restart Rails server
Problem: Routes not found (404)
Solution:
- Check
mount BreakEscape::Engine, at: '/break_escape'in routes.rb - Run
rails routes | grep break_escape - Restart Rails server
Problem: Migrations don't run
Solution:
- Run
rails break_escape:install:migrations - Check
db/migrate/for copied migrations - Run
rails db:migrate - Verify with
rails db:migrate:status
Problem: "Player must exist" validation error
Solution:
- Ensure User model exists
- Check polymorphic association:
player_typeandplayer_idare set correctly - Verify:
BreakEscape::Game.create!(player: User.first, mission: ...)
Problem: Static assets return 404
Solution:
- Check files exist:
ls -la /path/to/BreakEscape/public/break_escape/ - Verify mount path matches:
mount BreakEscape::Engine, at: '/break_escape' - Restart Rails server
- Check
config.public_file_server.enabledin production.rb
Problem: CSRF token invalid
Solution:
- Verify
<%= csrf_meta_tags %>in layout - Check
window.CSRF_TOKENis set in JavaScript - Ensure API calls include
'X-CSRF-Token': csrfTokenheader - Verify
credentials: 'same-origin'in fetch calls
Problem: Ink scripts fail to load
Solution:
- Check
bin/inklecateexists and is executable:ls -la bin/inklecate - Test compilation manually:
bin/inklecate -o /tmp/test.json scenarios/ink/test.ink - Check file permissions
- Verify controller
resolve_and_compile_inklogic
Problem: Authorization errors (Pundit)
Solution:
- Ensure Pundit is installed:
gem 'pundit'in Gemfile - Check policies exist:
app/policies/break_escape/game_policy.rb - Verify
authorize @gamein controllers - Test policy:
BreakEscape::GamePolicy.new(user, game).show?
Problem: Tests fail with "table does not exist"
Solution:
- Run migrations in test environment:
RAILS_ENV=test rails db:migrate - Verify schema.rb includes BreakEscape tables
- Reset test database:
RAILS_ENV=test rails db:reset
Problem: Game state doesn't persist
Solution:
- Check
PUT /games/:id/sync_stateendpoint works - Verify
player_stateJSONB column exists - Test in console:
game.update!(player_state: {currentRoom: 'test'}) - Check logs for errors during API calls
Problem: Scenario randomization not working
Solution:
- Verify ERB templates exist:
app/assets/scenarios/*/scenario.json.erb - Check
Mission#generate_scenario_datamethod - Test:
mission.generate_scenario_datain console - Ensure
before_create :generate_scenario_datacallback in Game model
Verification Checklist
After integration, verify all these work:
- Engine mounts at
/break_escape - Mission listing page loads
- User can start a game
- Game redirects to
/break_escape/games/:id - API endpoint
/games/:id/bootstrapworks - API endpoint
/games/:id/scenarioworks - API endpoint
/games/:id/ink?npc=Xworks - API endpoint
/games/:id/sync_stateworks - API endpoint
/games/:id/unlockworks - Static assets load (JS, CSS, images)
- Ink scripts compile on-demand (~300ms)
- Scenario has unique passwords per game
- Game state persists across page reloads
- Authorization policies work (can't access other user's games)
- CSRF protection works
- Tests pass:
rails test - Database has 2 tables:
break_escape_missions,break_escape_games - Polymorphic player works (User or DemoUser)
- Multiple users can play simultaneously
Performance Monitoring
Monitor JIT Ink Compilation
Add logging to track compilation times:
Location: /path/to/BreakEscape/app/controllers/break_escape/games_controller.rb
def compile_ink(ink_path)
start_time = Time.now
# ... compilation logic ...
duration = ((Time.now - start_time) * 1000).round
Rails.logger.info "[BreakEscape] Compiled #{File.basename(ink_path)} in #{duration}ms"
end
Monitor logs:
tail -f log/production.log | grep "BreakEscape"
Expected: Compilation times under 500ms.
Monitor Database Performance
Check JSONB query performance:
-- Find slow queries
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
WHERE query LIKE '%break_escape_games%'
ORDER BY mean_exec_time DESC
LIMIT 10;
Ensure GIN indexes are used:
EXPLAIN ANALYZE
SELECT * FROM break_escape_games
WHERE player_state->>'currentRoom' = 'office';
Expected: Should show Bitmap Index Scan on break_escape_games_player_state_idx.
Rollback Plan
If integration fails, rollback:
Step 1: Remove routes
Location: /path/to/hacktivity/config/routes.rb
Comment out:
# mount BreakEscape::Engine, at: '/break_escape'
Step 2: Rollback migrations
rails db:rollback STEP=3
Step 3: Remove gem
Location: /path/to/hacktivity/Gemfile
Comment out:
# gem 'break_escape', path: '../BreakEscape'
Then:
bundle install
Step 4: Restart server
rails server
Next Steps After Integration
Once integrated successfully:
-
User Testing
- Invite beta users to play
- Gather feedback on gameplay
- Monitor for errors
-
Performance Tuning
- Add database indexes if queries are slow
- Cache compiled Ink scripts if needed
- Optimize scenario ERB generation
-
Analytics
- Track game completions
- Monitor average play time
- Identify difficult missions
-
Enhancements
- Leaderboards
- Save/load game states
- Achievements system
- Admin dashboard
-
Monitoring
- Set up error tracking (Sentry, Rollbar)
- Monitor server logs
- Track API response times
Configuration Reference
Environment Variables
Optional environment variables for BreakEscape:
# .env or config/application.yml
BREAK_ESCAPE_MOUNT_PATH=/break_escape # Default mount path
BREAK_ESCAPE_INK_COMPILE_TIMEOUT=5000 # Compilation timeout (ms)
BREAK_ESCAPE_MAX_GAMES_PER_USER=10 # Limit games per user
BREAK_ESCAPE_ENABLE_DEMO_USERS=false # Disable in production
BREAK_ESCAPE_LOG_LEVEL=info # debug, info, warn, error
Initializer (Optional)
Location: /path/to/hacktivity/config/initializers/break_escape.rb
# Optional configuration
BreakEscape.configure do |config|
# Enable/disable features
config.enable_demo_users = Rails.env.development?
# Limits
config.max_games_per_user = 10
config.ink_compile_timeout = 5000 # milliseconds
# Logging
config.log_ink_compilation = true
config.log_scenario_generation = true
end
Support and Documentation
BreakEscape Documentation
- 00_OVERVIEW.md - Project aims and decisions
- 01_ARCHITECTURE.md - Technical design
- 02_DATABASE_SCHEMA.md - Database reference
- 03_IMPLEMENTATION_PLAN.md - Implementation guide
- 04_API_REFERENCE.md - API endpoints
- 05_TESTING_GUIDE.md - Testing strategy
External Documentation
Success Criteria
Integration is successful when:
- ✅ All routes work without errors
- ✅ Users can create and play games
- ✅ Game state persists across sessions
- ✅ API endpoints validate correctly
- ✅ Static assets load properly
- ✅ Ink scripts compile on-demand
- ✅ Authorization prevents unauthorized access
- ✅ Tests pass in Hacktivity environment
- ✅ No errors in production logs
- ✅ Performance is acceptable (<500ms for most requests)
Conclusion
BreakEscape is now integrated into Hacktivity! 🎉
Users can:
- Browse missions at
/break_escape - Start games with unique passwords
- Play with persistent state
- Complete missions and track progress
The engine is:
- Self-contained (isolated namespace)
- Secure (server-side validation)
- Performant (JIT compilation ~300ms)
- Scalable (2-table architecture)
- Maintainable (well-tested, documented)
For ongoing support, refer to the documentation files and Rails Engine guides.
Version: 2.0 Last Updated: November 2025 Status: Ready for Production Maintained by: BreakEscape Team