Web UI API (/api/v1/web/*)#
Note on Code Examples: This document contains Python/FastAPI/Pydantic code examples for reference. When implementing in Rails, translate these patterns to Ruby/Rails/ActiveModel equivalents using appropriate Rails conventions (controllers, models, concerns, serializers, etc.).
These endpoints support the Rails/Hotwire dashboard that human users interact with. They power views, forms, toasts, and live updates via Turbo Frames and Turbo Streams. Agents do not use these endpoints. All list endpoints must support pagination and query filtering.
🧭 These endpoints define the backend interface needed to support the user-facing views described in Phase 3 - Web UI Foundation. As you implement the frontend (Phase 3), be sure to reference this section to ensure every view or modal maps to a corresponding route here. We recommend annotating Stimulus controllers with source endpoint comments and may add cross-references in Phase 3 to maintain that alignment. Some amount of the frontend has already been implemented and will need to be maintained and refactored as the backend is implemented.
Table of Contents#
- Web UI API (
/api/v1/web/*)
Authentication and Profile#
Includes endpoints for administrator management of users and project access rights.
💡 Note: Users can only update their own name and email. Role assignment and project membership changes are restricted to admins.
-
POST /api/v1/web/auth/login- Logintask_id:auth.login - Authentication for the web interface is handled by Devise with JWT tokens in the
Authorizationheader. Authorization is handled by CanCanCan in the authorization layer. -
POST /api/v1/web/auth/logout- Logouttask_id:auth.logout -
POST /api/v1/web/auth/refresh- Refresh JWT tokentask_id:auth.refresh -
GET /api/v1/web/auth/me- Profile detailstask_id:auth.me -
PATCH /api/v1/web/auth/me- Update name/emailtask_id:auth.update_me -
POST /api/v1/web/auth/change_password- Change passwordtask_id:auth.change_password -
GET /api/v1/web/auth/context- Get current user + project contexttask_id:auth.get_context- See Auth Context for details. -
POST /api/v1/web/auth/context- Switch active projecttask_id:auth.set_context- See Auth Context for details. -
GET /api/v1/web/users/- 🔐 Admin: list all users (paginated, filterable)task_id:auth.list_users- This uses the flowbite table component (see Table with Users for inspiration) and supports filtering and pagination. -
POST /api/v1/web/users/- 🔐 Admin: create usertask_id:auth.create_user -
GET /api/v1/web/users/{id}- 🔐 Admin: view user detailtask_id:auth.get_user -
PATCH /api/v1/web/users/{id}- 🔐 Admin: update user info or roletask_id:auth.update_user -
DELETE /api/v1/web/users/{id}- 🔐 Admin: deactivate or delete usertask_id:auth.delete_user -
GET /api/v1/web/projects/- 🔐 Admin: list all projectstask_id:auth.list_projects - POST /api/v1/web/projects/ - 🔐 Admin: create project
task_id:web.projects.create_project -
GET /api/v1/web/projects/{id}- 🔐 Admin: view project infotask_id:auth.get_project -
PATCH /api/v1/web/projects/{id}- 🔐 Admin: update name, visibility, user assignmenttask_id:auth.update_project- Users have a many-to-many relationship with projects throughProjectUserAssociationandProjectUserRole. -
DELETE /api/v1/web/projects/{id}- 🔐 Admin: archive projecttask_id:auth.delete_project- This should be a soft delete, and the project should be archived. - Audit existing endpoints in
/api/v1/webfor authentication/authorization. - This will be done in its own project.- See Authentication Cleanup for audit results and implementation plan.
- Ensure that all endpoints in
/api/v1/web, except for/api/v1/web/auth/login, require authentication using theget_current_userdependency. - Ensure that all endpoints in
/api/v1/webare protected by theuser_canfunction. - Ensure that all endpoints in
/api/v1/webcheck the currently selected project context (seetask_id:auth.get_context) and only return data for the currently selected project.
Campaign Management#
For additional notes on the campaign management, see Campaign Notes.
Model Requirements#
To fully support UI ordering, user-friendly attack summaries, and richer campaign lifecycle controls, the following model-level fields must be added or updated:
- Add
Attack.position: int- numeric ordering field within a campaigntask_id:model.attack.position - Add
Attack.comment: Optional[str]- user-provided description for UI displaytask_id:model.attack.comment - Add
Attack.complexity_score: Optional[int]- derived from keyspace or agent benchmarks, range 1-5task_id:model.attack.complexity_score - Optionally evolve
Campaign.active: boolintoCampaign.state: Enum(draft,active,archived, etc.) to support lifecycle toggles and clear workflow statestask_id:model.campaign.state_enum
These fields must be integrated into campaign detail responses, sortable/queryable in the DB layer, and respected in API output.
Implementation Tasks#
Note: Agent Configuration and Telemetry#
Some agent configuration and telemetry endpoints (e.g., fine-grained device toggles, temperature abort thresholds, hashcat backend flags) may not be fully supported by the current v1 Agent API.
These are earmarked for Phase 5 implementation when Agent API v2 is introduced.
Be sure to encapsulate such features behind feature flags or optional fields in the backend to avoid breaking compatibility.
Note: Agent Display Name Fallback#
logic:
display_name = agent.custom_label or agent.host_name
This fallback logic must be applied anywhere an agent is shown to the user. Include this behavior in both API response schemas and frontend component logic to ensure consistent display.
- Add
guid: UUID = uuid4()field toAttackResourceFileto enable export/import referencingtask_id:model.resource.guid_support - Add
POST /api/v1/web/campaigns/{id}/reorder_attacksto accept a list of attack IDs and persist ordertask_id:campaign.reorder_attacks - Add
POST /api/v1/web/attacks/{id}/movewith direction (up,down,top,bottom) to reposition relative to other attackstask_id:attack.move_relative(refactored to use service-layer only, no DB code in endpoint) - Add
POST /api/v1/web/attacks/{id}/duplicateto clone an attack in-placetask_id:attack.duplicate - Add
DELETE /api/v1/web/attacks/bulkto delete multiple attacks by IDtask_id:attack.bulk_delete - Add
POST /api/v1/web/campaigns/{id}/startandPOST /api/v1/web/campaigns/{id}/stopto manage lifecycle statetask_id:campaign.lifecycle_toggle - Add or enrich campaign attack view model to support: type label, length, friendly settings summary, keyspace, complexity, and user comments
task_id:campaign.attack_summary_viewmodel -
GET /api/v1/web/campaigns/- List campaigns (paginated, filterable). Should support JSON polling or SSE-driven update triggers to notify the browser when campaign progress changes and refresh the relevant list view.task_id:campaign.list_view -
POST /api/v1/web/campaigns/- Create a new campaigntask_id:campaign.create -
GET /api/v1/web/campaigns/{id}- Campaign detail view with attacks/taskstask_id:campaign.detail_view -
PATCH /api/v1/web/campaigns/{id}- Update campaigntask_id:campaign.update -
DELETE /api/v1/web/campaigns/{id}- Archive/delete campaigntask_id:campaign.archive_delete -
POST /api/v1/web/campaigns/{id}/add_attack- Add attack to campaigntask_id:campaign.add_attack -
GET /api/v1/web/campaigns/{id}/progress- Structure and return campaign progress/status JSON data for pollingtask_id:campaign.progress_data -
GET /api/v1/web/campaigns/{id}/metrics- Aggregate stats (see items 3 and 5 of Core Algorithm Implementation Guide)task_id:campaign.metrics_summary -
POST /api/v1/web/campaigns/{id}/relaunch- Relaunch attack if previously failed, or if any linked resource (wordlist, mask, rule) has been modified since the last run. Requires re-validation and explicit user confirmation.task_id:campaign.rerun_attack
Attack Management#
For additional notes on the attack editor UX, see Attack Notes.
UX Design Goals#
These design goals are for the attack editor modal and should be applied to the data sent to the client. Though not exclusively API-related, these are important considerations for the API implementation and will be finalized in Phase 3 with the full user interface implementation. After adding the appropriate supporting functionality to the models and endpoints, Skirmish should add stub components for these views with TODO comments to ease implementing phase 3 frontend.
Common Editing Features#
- Dynamically update keyspace and complexity score for unsaved changes
task_id:attack.ux_estimate_unpersisted - Show these values in attack editor view like campaign detail view
task_id:attack.ux_estimate_view - Warn and require confirmation when editing an
exhaustedorrunningattacktask_id:attack.ux_edit_lifecycle_reset - Confirming edit resets attack to
pendingand triggers reprocessingtask_id:attack.ux_edit_lifecycle_reset - Allow export/import of JSON files for Attack or Campaign (supports template reuse)
task_id:attack.ux_export_import_json
Dictionary Attack UX#
See New Dictionary Attack Editor for more details.
- Min/max length fields default to typical hash type range (e.g., 1-32)
task_id:attack.ux_dictionary_length_defaults - Wordlist dropdown with search, sorted by last modified, includes entry count
task_id:attack.ux_dictionary_wordlist_dropdown - "Modifiers" button group for non-expert users (e.g.,
+ Change Case,+ Substitute Characters) in dictionary attack editor (task_id:attack.ux_dictionary_modifiers) - Optional rule list dropdown for expert users
task_id:attack.ux_dictionary_rule_dropdown- This should be a Searchable Dropdown that allows the user to search for a rule by name and select one.
- Support "Previous Passwords" as dynamic wordlist option
task_id:attack.ux_dictionary_previous_passwords- This will automatically generate a dynamic wordlist from the previous project's cracked passwords. When this option is select, the wordlist dropdown and the option for ephemeral wordlist should be hidden. The dynamic wordlist is not persisted and is generated on the fly when the attack is run or rerun.
- Support ephemeral wordlist field with "Add Word" UI for small lists
task_id:attack.ux_dictionary_ephemeral_wordlist- This should be a Flowbite Text Input with a "+" button to add a new word. The words should be persisted in the attack resource file and used as a generated wordlist when the attack is run.
Mask Attack UX#
See New Mask Attack Editor for more details.
- Add/remove inline mask lines (ephemeral mask list)
task_id:attack.ux_mask_inline_lines - Validate mask syntax in real-time
task_id:attack.ux_mask_syntax_validation- This should follow hashcat's mask syntax restrictions. This applies to the mask input field implemented in
task_id:attack.ux_mask_inline_linesand the validation should be triggered when the user leaves the input field.
- This should follow hashcat's mask syntax restrictions. This applies to the mask input field implemented in
- Ephemeral mask list stored only with the attack, deleted when attack is removed
task_id:attack.ux_mask_ephemeral_storage - Add/remove inline mask lines (ephemeral mask list)
task_id:attack.ux_mask_inline_lines- This should be a Flowbite Text Input with a "+" button to add a new word. The masks should be persisted in the attack resource file and used as a generated mask list when the attack is run.
Brute Force UX#
See New Brute Force Attack Editor for more details.
- Checkbox UI for charset selection (
Lowercase,Uppercase,Numbers,Symbols,Space)task_id:attack.ux_brute_force_charset_selection- Each charset maps to a mask token for the
?1character (i.e.?lfor lowercase, etc.). The user should be able to select one or more charsets and the selected charsets should be used to generate the mask.
- Each charset maps to a mask token for the
- Range selector for mask length (min/max)
task_id:attack.ux_brute_force_length_selector- This should be a Flowbite Range Slider and it is used to set the
min_lengthandmax_lengthof the the incremental attack. - The max length is also used to set the number of
?1tokens in the mask (seetask_id:attack.ux_brute_force_mask_generation)
- This should be a Flowbite Range Slider and it is used to set the
- Automatically generate
?1?1?...style mask based on selected lengthtask_id:attack.ux_brute_force_mask_generation- The brute force attack is just a user friendly way to generate an increment attack with a mask. The mask is generated by the
?1token which is repeated for the length of the maximum character length (defined by themax_lengthintask_id:attack.ux_brute_force_length_selector). The selected character types are used to set the tokens used in the custom character set 1 for the attack.
- The brute force attack is just a user friendly way to generate an increment attack with a mask. The mask is generated by the
- Derive
?1charset from selected charsets (e.g.,?l?dfor lowercase + digits)task_id:attack.ux_brute_force_charset_derivation- This is determined from the character types chosen in
task_id:attack.ux_brute_force_charset_selectionand should be set as thecustom_charset_1for the attack.
- This is determined from the character types chosen in
Implementation Tasks#
Includes support for a full-featured attack editor with configurable mask, rule, wordlist resources; charset fields; and validation logic. Endpoints must power form-based creation, preview validation, reordering, and config visualization.
Note: See Attack Notes for more details on the attack editor UX and implementation.
- Implement
POST /attacks/validatefor dry-run validation with error + keyspace responsetask_id:attack.validate - Validate resource linkage: masks, rules, wordlists must match attack mode and resource type (task_id: resource-linkage-validation)
- Support creation via
POST /attacks/with full config validationtask_id:attack.create_endpoint - Return structured JSON validation error format on failed creation
task_id:attack.create_validation_error_format - Support reordering attacks in campaigns (if UI exposes it)
task_id:attack.reorder_within_campaign - Implement performance summary endpoint:
GET /attacks/{id}/performancetask_id:attack.performance_summary- This supports the display of a text summary of the attack's hashes per second, total hashes, and the number of agents used and estimated time to completion. See items 3 and 3b in the Core Algorithm Implementation Guide for more details. This should be live updated via SSE when the attack status changes (see
task_id:attack.live_updates_json).
- This supports the display of a text summary of the attack's hashes per second, total hashes, and the number of agents used and estimated time to completion. See items 3 and 3b in the Core Algorithm Implementation Guide for more details. This should be live updated via SSE when the attack status changes (see
- Implement toggle:
POST /attacks/{id}/disable_live_updatestask_id:attack.disable_live_updates(now UI-only, not persisted in DB) - All views must return JSON data suitable for rendering
task_id:attack.json_data_svelte. - All views should support SSE/Turbo Stream auto-refresh triggers
task_id:attack.live_updates_svelte - SSE endpoints have been implemented on the backend to notify the client when attack status (progress, status, etc.) has changed. Frontend functionality will need to be implemented to handle the SSE events and update the UI accordingly using Turbo Streams and Stimulus controllers with EventSource
- Add human-readable formatting for rule preview (e.g., rule explanation tooltips)
task_id:attack.rule_preview_explanation- This is implemented in
task_id:attack.rule_preview_explanationon the backend and displays a tooltip with the rule explanation when the user hovers over the rule name in the rule dropdown. See Rule Explanation for more details.
- This is implemented in
- Implement default value suggestions (e.g., for masks, charset combos)
task_id:attack.default_config_suggestions- This is implemented in
task_id:attack.default_config_suggestionson the backend and displays a dropdown of suggested masks, charsets, and rules for the attack. See Default Config Suggestions for implementation details and specific tasks.
- This is implemented in
All views should support SSE triggers or polling to allow dynamic refresh when agent-submitted updates occur.
-
GET /api/v1/web/attacks/- List attacks (paginated, searchable)task_id:attack.list_paginated_searchable -
POST /api/v1/web/attacks/- Create attack with config validationtask_id:attack.ux_created_with_validation- This supports the creation of a new attack with validation of the attack's config using ActiveModel validations.
-
GET /api/v1/web/attacks/{id}- View attack config and performancetask_id:attack.ux_view_config_performance- This supports the display of the attack's config and performance information in a modal when the user clicks on an attack in the campaign detail view.
-
PATCH /api/v1/web/attacks/{id}- Edit attacktask_id:attack.ux_edit_attack- This supports the editing of the attack's config in a modal when the user clicks on an attack in the campaign detail view.
-
DELETE /api/v1/web/attacks/{id}- Delete attacktask_id:attack.ux_delete_attack- This deletes an attack from the campaign. The attack should be removed from the campaign. If the attack has not been started, it should be deleted from the database.
- If the attack has been started, it should be marked as deleted and the attack should be stopped.
- Any ephemeral resources should be deleted from deleted attacks, but non-ephemeral resources should be unlinked from the attack.
-
POST /api/v1/web/attacks/validate- Return validation errors or keyspace estimate (see Core Algorithm Implementation Guide)task_id:attack.validate_errors_keyspace -
GET /api/v1/web/attacks/{id}/performance- Return task/agent spread, processing rate, and agent participation for a given attack.- Used to diagnose bottlenecks or performance issues by surfacing which agents worked the task, their individual speed, and aggregate throughput.
- Useful for verifying whether a slow campaign is due to insufficient agent coverage or unexpectedly large keyspace.
task_id:attack.performance_diagnostics
Hash List Management#
Hash lists are a collection of hashes that can be used in a campaign. They are a fundemental core component of CipherSwarm and are the primary way to manage hashes within a project. When a hash list is added to the system, either through the upload of a file containing hashes or through the creation of a new hash list, the hashes are stored in the database as hash items and are associated with the hash list. When a campaign is created, the hash list can be selected and the hashes in the hash list will be the focus of the attacks. When an agent requests a task, which is a subset of an attack, the agent will receive a dynamically generated download file containing the uncracked hashes from the hash list in a format that is compatible with hashcat. As the agent processes the hashes, the agent will submit the cracked hashes back to the system and the system will update the hash list with the cracked hashes. If another agent successfully cracks a hash that is part of a hash list that is being worked by another agent, all agents working on the hash list will be notified and directed to download a zap list, which contains the hashes that have been cracked since the they downloaded the hash list. The agent portion of this process is handled by the Agent API, here we will be implementing the endpoints for the user interface to manage hash lists.
Since hash lists are one of the most sensitive components of the system, they are always restricted to a specific project and are never visible to other projects. This is enforced by the system and the user interface should reflect this restriction. If an attack successfully cracks a hash that appears in multiple hash lists across different projects, the hash item in each project will be updated with the found plaintext value, but it is not revealed to the user what attack found the plaintext (unlike when a hash is cracked in a campaign that is part of the user's project). This is to avoid leaking information about password reuse across multiple projects.
UX Design Goals#
- Add
POST /api/v1/web/hash_lists/- Create a new hash listtask_id:hash_list.create -
GET /api/v1/web/hash_lists/- List hash lists (paginated, searchable)task_id:hash_list.list_paginated_searchable -
GET /api/v1/web/hash_lists/{id}/items- List hash items in hash listtask_id:hash_list.list_items- This should return a paginated list of hash items in the hash list and should be searchable by hash value, plaintext, and status. The status is simply cracked or uncracked, depending on whether plaintext is found or not. The list should be exportable as TSV and CSV files. -
GET /api/v1/web/hash_lists/{id}- View hash listtask_id:hash_list.view -
PATCH /api/v1/web/hash_lists/{id}- Update hash listtask_id:hash_list.update -
DELETE /api/v1/web/hash_lists/{id}- Delete hash listtask_id:hash_list.delete
Save/Load Schema Design#
CipherSwarm should allow saving and loading of both individual Attacks and entire Campaigns via a custom JSON format. This will support backup, sharing, and preconfiguration workflows.
🔐 The exported format must include:
- All editable fields of the attack or campaign, including position and comment
- For campaigns: the order of attached attacks must be preserved
- Any ephemeral word or mask lists used by an attack must be serialized as inline content
- For linked non-ephemeral resources (wordlists, masks, rules), include their stable UUID (
guid) for re-linking
🚫 The exported format must not include:
- Project ID or User ID bindings
- Internal database primary keys
📥 On import:
- The backend must validate schema correctness
- Rehydrate ephemeral resources directly
- Look up non-ephemeral resources by GUID (
guid) - Prompt the user or fail gracefully if a GUID reference does not resolve
This schema should be versioned and tested against a validation spec.
A new guid: UUID field must be added to the AttackResourceFile model to support this. The GUID should be unique, stable across sessions, and used as the canonical identifier for serialization workflows. It should be generated using uuid4() at the time the resource is first created, and must remain immutable and internal-only (not exposed to Agent APIs).
If a resource referenced by GUID cannot be matched during import (either due to deletion or lack of permission), the user must be prompted with three fallback options:
- Select a Replacement - Choose a compatible resource of the same
resource_typefrom a dropdown - Skip Affected Attack - Import the rest of the campaign, omitting attacks missing required resources
- Abort Import - Cancel the import entirely
All fallback logic should be implemented server-side with support for frontend prompting.
Ephemeral Resources#
Attacks can support ephemeral resources that can be created and edited within the attack editor. These resources are still persisted to the database, but they are stored as part of an AttackResourceFile record as a JSON structure, and are not visible in the Resource Browser or available for reuse in other attacks. They are deleted when the attack is deleted, they do not have a backing file in S3, and they are exported inline if the attack is exported within the same JSON file. The Agent API will provide a non-signed URL to agents to access these resources when the attack is running and they are downloaded directly from CipherSwarm, rather than the MinIO service.
🧩 Extended Implementation Tasks#
These tasks expand the attack editing interface and logic to support contextual UIs, one-off resources, and user-friendly modifiers without exposing raw hashcat details.
- Implement
POST /api/v1/web/attacks/estimateto return keyspace + complexity score for unsaved attack inputtask_id:attack.estimate_unpersisted(see Phase 2 Implementation Tasks) - Add edit-protection logic to warn if attack is
runningorcompletedbefore allowing edit (Web UI API)task_id:attack.edit_protect - Support ephemeral inline wordlists (multiple
add wordfields) stored in memory or DB during attack creation, deleted when the attack is deleted. (Web UI API)task_id:attack.ephemeral_wordlist - Support ephemeral inline masks (
add maskline interface) with same lifecycle behaviortask_id:attack.ephemeral_masklist(see section Ephemeral Resources above) -
task_id:attack.modifier_ui_to_rulesImplement "Modifiers" UI: Map UI modifier buttons to rule file UUIDs for dictionary attacks (seenotes/ui_screens/new_dictionary_attack_editor.md,attack.md). Ensure the mapping is robust, testable, and extensible. Add tests for all supported modifiers. - Dictionary attack UI must support: min/max length, searchable wordlist dropdown (sorted by last modified), option to use dynamic wordlist from previous project cracks
task_id:attack.dictionary_ui_controls- See Dictionary Attack UX for more details.
- Brute force UI must allow checkbox-driven charset selection, range selector, and generate corresponding
?1?1?...style masks and?1custom charsettask_id:attack.brute_force_ui_logic(strong typing enforced) - Add support to export any single Attack or entire Campaign to a JSON file
task_id:attack.export_json - Add support to load campaign or attack JSON file and prefill the editor
task_id:attack.import_json_schema- See Save/Load Schema Design for more details.
The attack editor must support a modal-based, multi-form interface with per-attack-type customization. It should dynamically update keyspace estimates and complexity scores as the user changes input.
- ⚙️ The editor must show real-time keyspace and complexity values, even for non-persisted attacks. Backend support is needed for live estimation.
- ⚠️ Editing an attack that is already running or exhausted must trigger a confirmation prompt and reset the state if confirmed. This restarts the attack lifecycle.
- ✍️ User-facing options should be simplified (e.g. "+ Change Case") and map to hashcat rule resources internally.
- 🔁 Certain attack types (e.g., dictionary, brute force) must support one-off word/mask lists that are ephemeral and attack-local.
Agent Management#
For additional notes on the agent management, see Agent Notes.
🧩 UX Design Goals#
🖥️ Agent List View#
-
Everyone can see all agents and their state
-
Admin-only gear menu (Disable Agent / View Details)
-
Columns:
- Agent Name + OS
- Status
- Temp (°C) and Utilization (avg of enabled devices)
- Current and Average Attempts/sec
- Current Job (Project, Campaign, Attack)
➕ Agent Registration#
-
Modal to enter label and select Projects (multi-toggle)
- Upon creating a new agent, the UI must immediately display the generated token for copy/paste. Tokens are only shown once.
-
Upon save, display generated token to admin
⚙️ Agent Detail Tabs#
🔧 Settings
display_name = agent.custom_label or agent.host_name- Toggle: Enabled/Disabled
- Agent Update Interval (sec, default randomized 1-15)
- Toggle: Use Native Hashcat (sets
AdvancedAgentConfiguration.use_native_hashcat = true) - Toggle: Enable Additional Hash Types (
--benchmark-all) - List: Project assignment toggles
- Static: OS, IP, Signature, Token
🖥️ Hardware
-
List of backend devices from
--backend-info -
Toggle each device on/off → updates
backend_device -
Device toggling prompts: Apply now / Next Task / Cancel if task is running
-
Show gray placeholder if no devices reported yet
-
HW Settings:
-
--hwmon-temp-abort(note only, default to 90°C)- Note: This field is not yet supported by the v1 Agent API, but could be added safely to
AdvancedAgentConfigurationas an optional key. The agent will ignore unknown fields.
- Note: This field is not yet supported by the v1 Agent API, but could be added safely to
-
OpenCL Device Type selector (
opencl_devices)
-
-
Backend toggles: CUDA, HIP, Metal, OpenCL → affect
--backend-ignore-*
📈 Performance
- Line chart of
DeviceStatus.guess_rateover time (8hr window) - Donut chart per device within card: Utilization as a percentage (or N/A)
- Live updating via SSE
🪵 Log
- Timeline of
AgentErrorentries - Color-coded severity
- Fields: message, code, task link, details
🧠 Capabilities
- Pulls from
HashcatBenchmark - Table (rollup view): Toggle / Hash ID / Name / Speed / Category
- Expandable rows per device
- Filterable + searchable
- Caption with benchmark timestamp
- Header button to trigger benchmark (sets
agent.state = pending)
🧩 Implementation Tasks#
-
GET /api/v1/web/agents/- List/filter agentstask_id:agent.list_filter- This will display a paginated, filterable datatable of all agents, with search and state filter. Used for the main agent management view.
task_id:agent.list_filter
- This will display a paginated, filterable datatable of all agents, with search and state filter. Used for the main agent management view.
-
GET /api/v1/web/agents/{id}- Detail viewtask_id:agent.detail_view- This will display a detailed view of the agent as described in the Agent Detail Tabs section.
-
PATCH /api/v1/web/agents/{id}- Toggle enable/disabletask_id:agent.toggle_state- This will be a toggle in the list of agents that changes the agent's
enabledstate and prevents the agent from picking up new tasks.
- This will be a toggle in the list of agents that changes the agent's
-
GET /api/v1/web/agents/{id}/benchmarks- View benchmark summarytask_id:agent.benchmark_summary- This will display a summary of the agent's benchmark results as described in the Agent Detail Tabs section. See also Agent Benchmark Compatibility for more details.
-
POST /api/v1/web/agents/{id}/test_presigned- Validate URL accesstask_id:agent.presigned_url_test- This will validate the presigned URL for the agent. It should take a
urlas a parameter and return a boolean value indicating whether the URL is valid. See Phase 2b: Resource Management for more details. See Presigned URL Test for more details.
- This will validate the presigned URL for the agent. It should take a
-
PATCH /api/v1/web/agents/{id}/config- UpdateAdvancedAgentConfigurationtoggles (backend_ignore, opencl, etc.)task_id:agent.config_update -
PATCH /api/v1/web/agents/{id}/devices- Toggle individual backend devices (stored as stringified int list)task_id:agent.device_toggle- This will toggle the individual backend devices for the agent. It should take a
devicesas a parameter and update thebackend_devicefor the agent. - The backend devices are stored in CipherSwarm on the Agent model as
list[str]of their descriptive names inAgent.devicesand the actual setting of what should be enabled is a comma-seperated list of integers, 1-indexed, so it'll be a little weird to figure out. We'll probably need a better way to do this in the future, but this is a limitation of v1 of the Agent API. See Hardware above for more details.
- This will toggle the individual backend devices for the agent. It should take a
-
POST /api/v1/web/agents/{id}/benchmark- Trigger new benchmark run (set topending)task_id:agent.benchmark_trigger- This changes the agent's state to
pending, which causes the agent to run a benchmark. See Agent Benchmark Compatibility for more details.
- This changes the agent's state to
-
GET /api/v1/web/agents/{id}/errors- Fetch structured log streamtask_id:agent.log_stream- This will fetch the structured log stream for the agent. It should return a list of
AgentErrorentries as described in the Logs section of the Agent Detail Tabs above. The log stream should be updated in real-time as new errors are reported and should use a human-readable visual style, color-coding, etc.
- This will fetch the structured log stream for the agent. It should return a list of
-
GET /api/v1/web/agents/{id}/performance- Stream guesses/sec time series for agent devices as Pydantic models (task_id:agent.web_agents_performance_data)- This will stream the guesses/sec time series for the agent. It should return a list of
DeviceStatusentries as described in the Agent Detail Tabs section. - This will be used to populate Flowbite Charts using the ApexCharts library. See Agent Performance Graph above for more details.
- This will stream the guesses/sec time series for the agent. It should return a list of
- Generate time series storage and reduction service for the AgentDevicePerformance model, including:
- SQLAlchemy model for time series device performance
- Service for storing and reducing time series data (bucketed averages)
- Integration with agent performance data endpoint
- Tests for time series storage and reduction
task_id:agent.agent_device_performance_timeseries
- Refactor the endpoint created in
task_id:agent.web_agents_performance_datato use the times series data generated intask_id:agent.agent_device_performance_timeseriestask_id:agent.refactor_performance_endpoint - Add a call to
record_agent_device_performanceinapp/core/services/agent_service.pyfromsubmit_task_status_serviceinapp/api/v1/endpoints/agent/tasks.pytask_id:agent.add_timeseries_call- This will record the agent device performance timeseries data when a task status is submitted. -
POST /api/v1/web/agents- Register new agent + return tokentask_id:agent.create- This will register a new agent and return a token for the agent. See Agent Registration above for more details.
-
GET /api/v1/web/agents/{id}/hardware- Report backend devices, temp limits, platform support flagstask_id:agent.hardware_detail- This will report the backend devices, temp limits, and platform support flags for the agent. See Hardware above for more details.
-
PATCH /api/v1/web/agents/{id}/hardware- Update hardware limits + platform togglestask_id:agent.hardware_update- This will update the hardware limits and platform toggles for the agent. See Hardware above for more details.
-
GET /api/v1/web/agents/{id}/capabilities- Show benchmark results (table + graph)task_id:agent.capabilities_table- This will show the benchmark results for the agent. See Agent Capabilities above for more details. See also Agent Benchmark Compatibility for more details.
Includes real-time updating views, hardware configuration toggles, performance monitoring, and error visibility. Most endpoints should use JSON requests and SSE triggers to refresh data without full page reloads. should be supported on list and detail views for dynamic agent status refresh.*
-
GET /api/v1/web/agents/- List/filter agentstask_id:agent.list_filter- This will display a paginated, filterable datatable of all agents, with search and state filter. Used for the main agent management view.
task_id:agent.list_filter
- This will display a paginated, filterable datatable of all agents, with search and state filter. Used for the main agent management view.
-
GET /api/v1/web/agents/{id}- Detail viewtask_id:agent.detail_view- This will display a detailed view of the agent as described in the Agent Detail Tabs section.
-
PATCH /api/v1/web/agents/{id}- Toggle enable/disabletask_id:agent.toggle_state- This will be a toggle in the list of agents that changes the agent's
enabledstate and prevents the agent from picking up new tasks.
- This will be a toggle in the list of agents that changes the agent's
-
POST /api/v1/web/agents/{id}/requeue- Requeue failed tasktask_id:agent.manual_requeue -
GET /api/v1/web/agents/{id}/benchmarks- View benchmark summarytask_id:agent.benchmark_summary- This will display a summary of the agent's benchmark results as described in the Agent Detail Tabs section. See also Agent Benchmark Compatibility for more details.
-
POST /api/v1/web/agents/{id}/test_presigned- Validate URL accesstask_id:agent.presigned_url_test
Resource Browser#
CipherSwarm uses AttackResourceFile objects to represent reusable cracking resources such as mask lists, wordlists, rule files, and custom charsets. All uploads go through the CipherSwarm backend, which creates the database record and issues a presigned S3 upload URL. No object in storage should exist without a matching DB entry. Each file includes a declared resource_type that drives editor behavior, validation rules, and allowed usage in attacks.
Line-oriented resources (masks, rules, small wordlists) may be edited interactively in the Web UI. Each line is validated individually and exposed via a dedicated endpoint. Larger files must be downloaded, edited offline, and reuploaded.
🧩 Implementation Tasks#
-
Implement
AttackResourceFile.resource_typewith enum (mask_list,rule_list, etc.)task_id:resource.define_enum -
Store and expose
line_format,line_encoding,used_for_modes,sourcetask_id:resource.augment_metadata -
Expose line-count and byte-size metadata for edit gating
task_id:resource.expose_editability_metrics -
Validate allowed attack usage based on resource type
task_id:resource.enforce_attack_mode_constraints -
Reject in-browser editing of resources over configured size/line threshold (configurable)
task_id:resource.edit_limit_check- Add
RESOURCE_EDIT_MAX_SIZE_MBandRESOURCE_EDIT_MAX_LINESsettings toconfig.py
- Add
-
Create line-editing endpoints:
task_id:resource.line_api_endpoints- This will create the line-editing endpoints for the resource. It should be a set of endpoints that allow the user to add, edit, and delete lines in the resource. This is different than the ephemeral attack resources, which are for very small resources that are used directly in attacks. This is an in-editor experience for editing larger, previously uploaded attack resource files. See Line-Oriented Editing for more details.
-
GET /resources/{id}/lines- This will return a list of lines in the resource. It should be a paginated list ofResourceLineobjects as described in the Resource Line Editing section. -
POST /resources/{id}/lines- This will add a new line to the resource. It should take alineas a parameter and add it to the resource. -
PATCH /resources/{id}/lines/{line_id}- This will update an existing line in the resource. It should take aline_idand alineas parameters and update the line in the resource. -
DELETE /resources/{id}/lines/{line_id}- This will delete an existing line in the resource. It should take aline_idas a parameter and delete the line from the resource.
-
Add model:
ResourceLineValidationErrortask_id:resource.line_validation_model- This will be a view model or serializer used to validate the lines in the resource. It should return structured JSON validation errors conforming to Rails conventions for API responses.
-
Validate line syntax per type (
mask_list,rule_list) and return structured JSONtask_id:resource.validate_line_content- This will validate the lines in the resource. It should return a list of
ResourceLineValidationErrorobjects conforming to idiomatic Rails validation error responses. It should use the same validation functionality as the attack editor line validation using for ephemeral attack resources.
- This will validate the lines in the resource. It should return a list of
-
Return
204 No Contenton valid edits,422JSON otherwisetask_id:resource.line_edit_response -
Disable editing for
dynamic_word_listand oversize filestask_id:resource.edit_restrictions- This will disable editing for
dynamic_word_listand oversize files. It should be a boolean flag that is used to determine if the resource can be edited.
- This will disable editing for
-
Support inline preview and batch validation (
?validate=true)task_id:resource.line_preview_mode -
Ensure file uploads always create an
AttackResourceFile(via presign + DB insert)task_id:resource.upload_contract_enforcement- This will ensure that file uploads always create an
AttackResourceFile(via presign + DB insert). If the upload fails, it should raise an error and not create the database entry. Thecontentfield is not used in regular file uploads, but is used for the ephemeral attack resources.
- This will ensure that file uploads always create an
-
Implement orphan file audit to catch mislinked objects
task_id:resource.orphan_audit -
Detect
resource_typefrom user input during uploadtask_id:resource.detect_type_on_upload- This will detect the
resource_typefrom user input during upload. It should be a string that is used to determine theresource_typeof the resource.
- This will detect the
-
Store resource metadata in
AttackResourceFilefor frontend usetask_id:resource.persist_frontend_metadata- This will store the resource metadata in
AttackResourceFilefor frontend use. It should include theresource_type,line_count,byte_size, andsource, as well as theused_for_modes,line_encodingand which projects the resource is linked to or if it is unrestricted.
- This will store the resource metadata in
🧠 Attack resource files share a common storage and metadata model, but differ significantly in validation, UI affordances, and where they are used within attacks. To support this diversity while enabling structured handling, each resource must declare a resource_type, which drives editor behavior, validation rules, and attack compatibility.
Supported resource_type values (Rails implementation should use an enum or string constants):
# app/models/attack_resource_file.rb (example structure for reference)
class AttackResourceFile < ApplicationRecord
enum resource_type: {
mask_list: 'mask_list',
rule_list: 'rule_list',
word_list: 'word_list',
charset: 'charset',
dynamic_word_list: 'dynamic_word_list' # Read-only, derived from cracked hashes
}
end
Each AttackResourceFile model should include these attributes:
# Rails model attributes (for reference)
# resource_type: String (enum)
# used_for_modes: Array # Enforced compatibility with hashcat attack modes (stored as JSON or array column)
# line_encoding: String # "ascii" or "utf-8" - Affects validation + editor behavior
# line_format: String # "freeform", "mask", "rule", "charset"
# source: String # "upload", "generated", "linked"
Editor behavior must respect the declared type:
| Resource TypeEditable?Line FormatEncodingNotes | ||||
|---|---|---|---|---|
mask_list | ✅ | mask syntax | ASCII | one per line, validated mask syntax |
rule_list | ✅ | rule grammar | ASCII | strict per-line validation |
word_list | ✅* | freeform | UTF-8 | loose rules, allow unicode, strip control |
charset | ✅ | charset def | ASCII | e.g. custom1 = abc123, used in attacks |
dynamic_word_list | ❌ | N/A | UTF-8 | read-only, generated from cracked hashes |
(*) Editing of large word lists may be disabled based on configured size thresholds.
Uploads must be initiated via CipherSwarm, which controls both presigned S3 access and DB row creation. No orphaned files should exist. The backend remains source of truth for metadata, content type, and validation enforcement.
🚨 Validation errors for resource line editing should follow Rails conventions. Return 422 Unprocessable Entity with structured JSON for validation errors:
Example response for per-line validation:
{
"errors": [
{
"line_index": 3,
"content": "+rfoo",
"valid": false,
"message": "Unknown rule operator 'f'"
},
{
"line_index": 7,
"content": "?u?d?l?l",
"valid": false,
"message": "Duplicate character class at position 3"
}
]
}
Suggested Rails serializer structure (for reference):
# Rails serializer or view model (example)
class ResourceLineValidationError
attr_accessor :line_index, :content, :valid, :message
def initialize(line_index:, content:, valid: false, message:)
@line_index = line_index
@content = content
@valid = valid
@message = message
end
def as_json(options = {})
{
line_index: @line_index,
content: @content,
valid: @valid,
message: @message
}
end
end
This should be returned from:
GET /resources/{id}/lines?validate=truePATCH /resources/{id}/lines/{line_id}if content is invalid
For valid input, return 204 No Content or the updated data, which controls both presign + DB insert. No orphaned files should exist. The backend remains source of truth for metadata, content type, and validation enforcement.
🔒 All uploaded resource files must originate from the CipherSwarm backend, which controls presigned upload URLs and creates the corresponding database entry in the AttackResourceFile model (located in app/models/attack_resource_file.rb). There should never be a case where a file exists in the object store without a corresponding DB row. The S3-compatible backend is used strictly for offloading large file transfer workloads (uploads/downloads by UI and agents), not as an authoritative metadata source.
💡 The UI should detect resource type and size to determine whether inline editing or full download is allowed. The backend should expose content metadata to guide this decision, such as line_count, byte_size, and resource_type. The frontend may display masks, rules, and short wordlists with line-level controls; long wordlists or binary-formatted resources must fall back to download/reupload workflows.
Includes support for uploading, viewing, linking, and editing attack resources (mask lists, word lists, rule lists, and custom charsets). Resources are stored in an S3-compatible object store (typically MinIO), but CipherSwarm must track metadata, linkage, and validation. Users should be able to inspect and edit resource content directly in the browser via web-based interactions.
🔐 Direct editing is permitted only for resources under a safe size threshold (e.g., < 5,000 lines or < 1MB). Larger files must be downloaded, edited offline, and reuploaded. This threshold should be configurable via an environment variable or application setting (e.g., RESOURCE_EDIT_MAX_SIZE_MB, RESOURCE_EDIT_MAX_LINES) to allow for deployment-specific tuning.
Line-Oriented Editing#
For eligible resource types (e.g., masks, rules, short wordlists), the Web UI should support a line-oriented editor mode:
- Each line can be edited, removed, or validated individually.
- Validation logic should be performed per line to ensure syntax correctness (e.g., valid mask syntax, hashcat rule grammar).
- Inline editing should be driven via Stimulus controllers making fetch requests to the backend.
Suggested line-editing endpoints:
-
GET /api/v1/web/resources/{id}/lines- Paginated and optionally validated list of individual linestask_id:resource.line_api_endpoints -
POST /api/v1/web/resources/{id}/lines- Add a new linetask_id:resource.add_line -
PATCH /api/v1/web/resources/{id}/lines/{line_id}- Modify an existing linetask_id:resource.update_line -
DELETE /api/v1/web/resources/{id}/lines/{line_id}- Remove a linetask_id:resource.delete_line
The backend should expose a ResourceLine representation (Rails serializer or view model example):
# Rails view model or serializer (for reference)
class ResourceLine
attr_accessor :id, :index, :content, :valid, :error_message
def as_json(options = {})
{
id: @id,
index: @index,
content: @content,
valid: @valid,
error_message: @error_message
}
end
end
These may be backed by temporary parsed representations for S3-stored resources, cached in memory or a staging database table for edit sessions.
Includes support for uploading, viewing, linking, and editing attack resources (mask lists, word lists, rule lists, and custom charsets). Resources are stored in an S3-compatible object store (typically MinIO), but CipherSwarm must track metadata, linkage, and validation. Users should be able to inspect and edit resource content directly in the browser via Hotwire/Turbo interactions.
🔐 Direct editing is permitted only for resources under a safe size threshold (e.g., < 5,000 lines or < 1MB). Larger files must be downloaded, edited offline, and reuploaded. This threshold should be configurable via an environment variable or application setting (e.g., RESOURCE_EDIT_MAX_SIZE_MB, RESOURCE_EDIT_MAX_LINES) to allow for deployment-specific tuning.
-
GET /api/v1/web/resources/- Combined list of all resources (filterable by type)task_id:resource.list_all -
GET /api/v1/web/resources/{id}- Metadata + linkingtask_id:resource.get_by_id -
GET /api/v1/web/resources/{id}/preview- Small content previewtask_id:resource.preview -
GET /api/v1/web/resources/upload- Render form to upload new resourcetask_id:resource.upload_form -
POST /api/v1/web/resources/- Upload metadata, request presigned upload URLtask_id:resource.upload_metadata -
PATCH /api/v1/web/resources/{id}(task_id: web-resources-patch) -
GET /api/v1/web/resources/{id}/edit- View/edit metadata (name, tags, visibility)task_id:resource.edit_metadata -
PATCH /api/v1/web/resources/{id}- Update metadatatask_id:resource.update_metadata -
DELETE /api/v1/web/resources/{id}- Remove or disable resourcetask_id:resource.delete- If the resource is linked to an attack, return a 409 Conflict error, otherwise it should be a hard delete by deleting the resource from the database and the object store. Both should be done in a way that is atomic and does not leave the system in an inconsistent state. Both scenarios should be covered by integration tests. -
GET /api/v1/web/resources/{id}/content- Get raw editable text content (masks, rules, wordlists)task_id:resource.get_content -
PATCH /api/v1/web/resources/{id}/content- Save updated content (inline edit)task_id:resource.update_content- This should be a PATCH request that updates the content of the resource. It should apply to file-backed resources only, as there are already other endpoints for ephemeral resources. It should not allow editing of resources larger than the configured size threshold and it should trigger a restart of any attacks, running or completed, that are using the resource. -
POST /api/v1/web/resources/{id}/refresh_metadata- Recalculate hash, size, and linkage from updated filetask_id:resource.refresh_metadata- This should be a POST request that recalculates the hash, size, and linkage from the updated file. It should be a soft refresh, meaning it should not trigger a restart of any attacks, running or completed, that are using the resource.
UX Support and Utility#
Purpose#
This section defines endpoints used by the frontend to dynamically populate UI elements, fetch partials, and support dropdowns, summaries, and metadata helpers that don't belong to a specific resource type.
Implementation Tasks#
-
GET /api/v1/web/modals/agents- Populate agent dropdownstask_id:ux.populate_agents- This should return a list of agents with their name and status, based on the
Agentmodel. - Any authenticated user should be able to see all agents.
- This should return a list of agents with their name and status, based on the
-
GET /api/v1/web/modals/resources- Populate resource selectors (mask, wordlist, rule)task_id:ux.populate_resources- This should return a list of resources with their name, and type, based on the
AttackResourceFilemodel. It does not show dynamic wordlists or ephemeral resources and only shows resources that are linked to the current project (based on the Project context created intask_id:auth.get_context) or are unrestricted, unless the user is an admin. Should allow for filtering by resource type and name, sorted by most recently modified.
- This should return a list of resources with their name, and type, based on the
-
GET /api/v1/web/modals/hash_types- Populate hash type dropdownstask_id:ux.populate_hash_types- This should return a list of hash types with their name and numeric mode, based on the
HashModemodel. It should be filterable by name and mode and return a nullable confidence score for the guess service if the guess service was involved in populating the dropdown (to support the hash type override UI in tasktask_id:guess.hash_type_override_ui). Sort by confidence score descending (unless it is None), then by mode ascending.
- This should return a list of hash types with their name and numeric mode, based on the
-
GET /api/v1/web/dashboard/summary- Return campaign/task summary data as structured JSON containing aggregated data for the various dashboard widgetstask_id:ux.summary_dashboard- seedocs/v2_rewrite_implementation_plan/notes/ui_screens/dashboard-ux.mdfor the expected structure and widgets - Seedocs/v2_rewrite_implementation_plan/notes/ui_screens/dashboard-ux.mdfor the complete description of the dashboard and the widgets that should be supported. -
GET /api/v1/web/health/overview- System health snapshot (agents online, DB latency, task backlog)task_id:ux.system_health_overview- This is more focused on the functionality of the system than the components and should be a overview of the number of agents online, the number of campaigns, tasks, and hash lists, as well as their current status and performance metrics and any errors that have occurred with any of the various agents, campaigns, tasks, and hash lists. Seedocs/v2_rewrite_implementation_plan/notes/ui_screens/health_status_screen.mdfor the complete description of the health status screen and the components that should be supported. -
GET /api/v1/web/health/components- Detailed health of core services (MinIO, Redis, DB)task_id:ux.system_health_components- This should include the detailed health of the MinIO, Redis, and DB services and their status, including latency and errors. See Health Status Screen Notes for the complete description of the health status screen and the components that should be supported.
-
Implement basic cashews cache for the health overview and components
task_id:ux.system_health_cache- This should be configured using the cashews cache decorator and should be configured to cache the health overview and components for 60 seconds. The cache should support memory and redis backends, with the memory backend being the default. It should be configured inapp/core/config.pyassettings.CACHE_CONNECT_STRING, defaulting tomem://?check_interval=10&size=10000, and the connection string should be passed tocache.setupinapp/main.py. Caching should be disabled in tests. -
GET /api/v1/web/modals/rule_explanation- Return rule explanation datatask_id:ux.rule_explanation_modal- This should return data to populate a rule explanation modal, which is a modal that explains the rule syntax for the selected rule. It should be a modal that is triggered by a button in the UI. - See
docs/v2_rewrite_implementation_plan/notes/specific_tasks/rule_explanation.md
- This should return data to populate a rule explanation modal, which is a modal that explains the rule syntax for the selected rule. It should be a modal that is triggered by a button in the UI. - See
Crackable Uploads#
To streamline the cracking workflow for non-technical users, we should support uploading raw data (files or hash datas) and automating the detection, validation, and campaign creation process. The system must distinguish between:
- File uploads (e.g.,
.zip,.docx,.pdf,.kdbx) that require binary hash extraction - Pasted or raw hash text (e.g., lines from
/etc/shadow,impacket'ssecretsdump, Cisco config dumps)
In the case of pasted hashes, the system should automatically isolate the actual hash portion, remove surrounding metadata, and sanitize formatting issues. This includes stripping login expiration flags from shadow lines or extracting just the hash field from NTLM pairs.
Use cases include:
- Uploading a file (e.g.,
.zip,.pdf,.docx) to extract hashes - Pasting a hash from a source like
/etc/shadow, even if it includes surrounding shadow metadata
This process should:
-
Extract or isolate the hash automatically
-
Perform validation on the extracted hash (type, syntax, format)
-
Prevent campaign creation if the hash is malformed or would fail in hashcat
-
Detect the hash type
-
Validate hashcat compatibility
-
Provide preview/feedback of what will be created
-
Automatically create:
- A
HashList - A
Campaign - One or more preconfigured
Attacks
- A
Users can then launch the campaign immediately or review/edit first.
Implementation Tasks#
- Complete all tasks in
docs/v2_rewrite_implementation_plan/side_quests/crackable_uploads_plan.md``task_id:upload.crackable_uploads_side_quest - Implement
GET /api/v1/web/hash/guessendpoint for live hash validation and guessing via service layertask_id:guess.web_endpoint- Fully implemented with a backend service later in
app/core/services/hash_guess_service.pythat can be reused for this task.
- Fully implemented with a backend service later in
- Ensure Crackable Upload UI (implemented in
frontend/) uses guess response to validate pasted hashes before campaign creationtask_id:guess.integrate_into_crackable_uploads - Add hash type selection UI (implementedin
frontend/) allowing user to confirm or override guess results - should be a dropdown with the hash types and a button to confirm the guess with the dropdown populated from the modals endpoints, but prefiltered to only include the hash types identified by the guess service. Displayname-that-hashresults with confidence scores and let user manually adjust if neededtask_id:upload.hash_type_override_ui - Automatically generate dictionary attack with an emphemeral wordlist derived from the uploaded content when usernames or prior passwords are available from the uploaded content. Useful for NTLM pairs,
/etc/shadow, or cracked zip headerstask_id:upload.create_dynamic_wordlist - Add confirmation/preview screen before launching a generated campaign to the crackable upload UI (implemented in
frontend/). Shows detected hash type, parsed sample lines, and proposed attacks/resourcestask_id:upload.preview_summary_ui- (checkdocs/v2_rewrite_implementation_plan/notes/user_flows_notes.mdanddocs/v2_rewrite_implementation_plan/side_quests/salvage_templates.mdfor the expected structure and UI) -
POST /api/v1/web/uploads/- Upload file or pasted hash blobtask_id:upload.upload_file_or_hash- Nearly all of the wiring for this should have been completed as part of the crackable uploads side questtask_id:upload.crackable_uploads_side_questand will just need to be verified and wired up to the endpoints for the UI. A new uploaded resource file type will need to be created to store in the database for the duration of processing the upload into a hash list and campaign. If uploaded as a file, the endpoint should accept a file upload and return a presigned URL for the file to be uploaded to S3. If uploaded as a hash blob, the endpoint should accept a text blob and store it in the database as a temporary resource. The endpoint should return a 201 Created response with the ID of the uploaded resource file, along with an upload task ID that can be used to view the new upload processing progress in the UI. The campaign and associated hash list will need to be marked as unavailable until the upload and background processing tasks are fully complete, so that it can be reflected in the UI. -
GET /api/v1/web/uploads/{id}/status- Show analysis result: hash type, extracted preview, validation statetask_id:upload.show_analysis_result- This should return the status of the upload task, including the hash type, extracted preview, and validation state. It should also return the ID of the uploaded resource file, along with an upload task ID that can be used to view the new upload processing progress in the UI. Status information and task completion information should be returned for each step of the upload and processing process to reflect the current state in the UI. Ensure UI pages are wired up to the endpoints, the endpoints are mocked in the tests, and the UI follows Rails ViewComponent patterns. -
POST /api/v1/web/uploads/{id}/launch_campaign- Generate resources and create campaign with default attackstask_id:upload.launch_campaign -
GET /api/v1/web/uploads/{id}/errors- Show extraction errors or unsupported file type warningstask_id:upload.show_extraction_errors -
DELETE /api/v1/web/uploads/{id}- Remove discarded or invalid uploadtask_id:upload.delete_upload
Live Event Feeds (Server-Sent Events)#
These endpoints provide Server-Sent Events (SSE) streams that Stimulus controllers can subscribe to for real-time trigger notifications, prompting the client to issue targeted fetch requests or Turbo Stream updates. No data is pushed directly via SSE streams - only lightweight notification signals (e.g., { "trigger": "refresh" }) to inform the client that updated content is available.
SSE is preferred over WebSockets for this use case because:
- Simpler architecture: No Redis dependency or complex pub/sub infrastructure
- Better reliability: Browser handles reconnection automatically
- Easier testing: No external dependencies or connection management
- HTTP-based: Works through proxies and firewalls
- Perfect fit: One-way notifications are exactly what SSE is designed for
Core Infrastructure#
- ✅ Rails
ActionController::Livewith streaming for SSE endpoints - ✅ In-memory event broadcasting (no Redis required)
- ✅ JavaScript
EventSourceclient-side support via Stimulus controllers - ✅ Devise JWT-based auth and project scoping
Event Triggers by Feed#
campaigns: OnAttack,Task, orCampaignstate change;CrackResultsubmission.agents: On agent heartbeat, performance update, or error report.toasts: On newCrackResult; displayed via UI toast notification.
Implementation Tasks (SSE Event System)#
Core Event Infrastructure#
- Create event broadcasting service (
app/core/services/event_service.py)task_id:sse.event_service- Implement in-memory event broadcaster with topic-based subscriptions
task_id:sse.memory_broadcaster - Add event listener registration/deregistration
task_id:sse.listener_management - Support project-scoped event filtering
task_id:sse.project_scoping
- Implement in-memory event broadcaster with topic-based subscriptions
SSE Endpoint Routes#
Each route provides an SSE stream for specific event types:
-
GET /api/v1/web/live/campaigns- Campaign/attack/task state changestask_id:sse.campaign_feed -
GET /api/v1/web/live/agents- Agent status, performance, and error updatestask_id:sse.agent_feed -
GET /api/v1/web/live/toasts- New crack results and system notificationstask_id:sse.toast_feed
Event Broadcasting Integration#
- Create service-layer event triggers
task_id:sse.service_triggers- Trigger campaign events on
Attack,Task,Campaignupdatestask_id:sse.campaign_triggers - Trigger agent events on
Agent,DeviceStatus,AgentErrorchangestask_id:sse.agent_triggers - Trigger toast events on
CrackResultsubmissiontask_id:sse.toast_triggers
- Trigger campaign events on
Authorization & Security#
- Enforce JWT authentication on SSE connections
task_id:sse.jwt_auth - Implement project-based event filtering
task_id:sse.project_filtering - Add connection timeout and cleanup
task_id:sse.connection_cleanup
Event Message Format#
Standard lightweight event format:
{
"trigger": "refresh",
"timestamp": "2024-01-01T12:00:00Z"
}
Optional targeted events:
{
"trigger": "refresh",
"target": "campaign",
"id": 123
}
Supporting Infrastructure#
File Layout#
app/
├── controllers/
│ └── api/
│ └── v1/
│ └── web/
│ └── live_controller.rb # SSE endpoints for /api/v1/web/live/*
├── services/
│ └── event_service.rb # In-memory event broadcasting
Module Responsibilities#
-
api/v1/web/live_controller.rb- Rails SSE route handlers using
ActionController::Liveand streaming - Authentication via
before_actionwith Devise - Project scoping and event filtering
- Rails SSE route handlers using
-
services/event_service.rb- In-memory event broadcaster
- Topic-based subscription management
- Event listener lifecycle management
- Project-scoped event filtering
Migration from WebSocket Implementation#
- Remove WebSocket dependencies and Redis pub/sub code
task_id:sse.cleanup_websocket - Update existing broadcast functions to use new event service
task_id:sse.migrate_broadcasts - Remove WebSocket-related gems from Gemfile
task_id:sse.remove_websocket_deps - Update tests to use SSE instead of WebSocket connections
task_id:sse.update_tests
Documentation Updates#
With the many changes to the API, we need to update the documentation to reflect the changes. These changes go beyond just the SSE implementation, but also include the new endpoints and changes to the existing endpoints, as well as changes to the UI and user flows documented in docs/v2_rewrite_implementation_plan/side_quests/salvage_templates.md and docs/v2_rewrite_implementation_plan/notes/user_flows_notes.md. The changes to be made are:
- Update the architecture documentation to reflect the changes. (found in
docs/architecture/*.md) - Update the API reference documentation to reflect the structure of the API and the endpoints that are available. (found in
docs/api/overview.mdanddocs/development/api-reference.md) - Update the user guide to reflect the changes. (found in
docs/user-guide/*.md) - Update the developer guide to reflect the changes. (found in
docs/development/*.md) - Update the getting started guide to reflect the changes. (found in
docs/getting-started/*.md) - Update the troubleshooting guide to reflect the changes. (found in
docs/user-guide/troubleshooting.md) - The primary audience for this guide is the administrator configuring the system, and the end user trying to figure out why their campaign is behaving as expected. It is not for developers and each section should be a single topic with a clear title and a description of the problem and the solution. - Update the FAQ to accurately reflect the current state of the system. (found in
docs/user-guide/faq.md)
Additional Tasks#
- Add robust unit tests for KeyspaceEstimator covering all attack modes and edge cases
- Ensure all estimation logic (AttackEstimationService, endpoints, tests) uses strong typing (ActiveModel validations, enums, value objects) instead of hashes/untyped data for attack/resource parameters