Skip to content

Dev#422

Merged
mayanayza merged 28 commits intomainfrom
dev
Jan 4, 2026
Merged

Dev#422
mayanayza merged 28 commits intomainfrom
dev

Conversation

@mayanayza
Copy link
Collaborator

No description provided.

mayanayza and others added 28 commits December 28, 2025 21:00
  Implement user API keys that allow programmatic access to the API with
  configurable permissions and network access restrictions.

  User API key features:
  - Create keys with permissions up to user's own level (Viewer/Member/Admin/Owner)
  - Scope keys to specific networks (subset of user's network access)
  - Key rotation endpoint (POST /api/auth/keys/{id}/rotate)
  - Enable/disable keys without deletion
  - Expiration dates for time-limited access
  - Bearer token authentication (Authorization: Bearer scp_u_...)

  OpenAPI documentation:
  - Add user_api_key security scheme alongside session and daemon_api_key
  - Document auth requirements per endpoint

  Auth system refactoring:
  - Replace legacy AuthenticatedUser with Authorized<IsUser>
  - Replace legacy AuthenticatedDaemon with Authorized<IsDaemon>
  - Add helper methods: daemon_id(), require_user_id(),
    require_organization_id(), require_permissions()
  - Add AuthMethod enum for audit logging

  Integration tests:
  - Permission escalation prevention
  - Key rotation (old key invalidated, new key works)
  - Expired/disabled key rejection
  - Network access enforcement- Owner isolation (users can't access other users' keys)
  Implement user API keys that allow programmatic access to the API with
  configurable permissions and network access restrictions.

  User API key features:
  - Create keys with permissions up to user's own level (Viewer/Member/Admin/Owner)
  - Scope keys to specific networks (subset of user's network access)
  - Key rotation endpoint (POST /api/auth/keys/{id}/rotate)
  - Enable/disable keys without deletion
  - Expiration dates for time-limited access
  - Bearer token authentication (Authorization: Bearer scp_u_...)

  OpenAPI documentation:
  - Add user_api_key security scheme alongside session and daemon_api_key
  - Document auth requirements per endpoint

  Auth system refactoring:
  - Replace legacy AuthenticatedUser with Authorized<IsUser>
  - Replace legacy AuthenticatedDaemon with Authorized<IsDaemon>
  - Add helper methods: daemon_id(), require_user_id(),
    require_organization_id(), require_permissions()
  - Add AuthMethod enum for audit logging

  Integration tests:
  - Permission escalation prevention
  - Key rotation (old key invalidated, new key works)
  - Expired/disabled key rejection
  - Network access enforcement- Owner isolation (users can't access other users' keys)
  - Add X-Content-Type-Options: nosniff to prevent MIME type sniffing
  - Add Referrer-Policy: strict-origin-when-cross-origin to limit referrer leakage
  - Add CSP frame-ancestors 'self' globally to prevent clickjacking
  - Add HSTS header when use_secure_session_cookies is enabled (HTTPS mode)

  Update share embed handling to use CSP frame-ancestors instead of
  X-Frame-Options, allowing more granular control:
  - Orgs with embed feature: frame-ancestors based on allowed_domains
  - Orgs without embed feature: frame-ancestors 'none'
  API Versioning:
  - Move all entity routes from /api/ to /api/v1/
  - Keep auth routes unversioned at /api/auth (session management)
  - Add /api/version endpoint returning api_version and server_version
  - Include ApiMeta (api_version, server_version) in all API responses

  User API Key Billing:
  - Add ApiKeyFeature check requiring api_access plan feature
  - Gate all user API key endpoints behind RequireFeature<ApiKeyFeature>
  - Check organization has API access during user API key authentication
  - Move from macro-generated handlers to explicit handlers

  Auth Middleware Cleanup:
  - Remove unused has_min_permission() and auth_method() methods
  - Remove email field from ApiKey variant (not needed for API key auth)

  Other:
  - Remove daemon compatibility tests and fixtures (v0.12.8)
  - Update UI API client to handle new meta field in responses
  - Update all UI components for /api/v1/ route changes
…_id caching

  - Add HTTP client timeouts (10s connect, 30s request) to fail fast on
    unreachable servers instead of hanging indefinitely

  - Add clear error messages for connection failures:
    - Connection refused: suggests checking server URL
    - Connect timeout: suggests checking firewall
    - Response timeout: suggests switching to Pull mode

  - Refactor registration to use daemon_id via announce_startup as the
    source of truth for registration status, not local host_id cache

  - Auto re-register when server doesn't recognize daemon (e.g., after
    server database reset or daemon deletion)

  - Remove local host_id caching entirely - server already provides
    host_id in discovery requests via DiscoveryType

  - Fix API paths to use legacy /api/daemons/ routes for compatibility
- Track sortState.field and sortState.direction individually in save effect
- Previously only tracked object reference which missed property mutations

Closes #406
… topology (#409)

When creating bindings for ALL_INTERFACES_IP (0.0.0.0), use interface IDs
from the deduplicated `interfaces` list rather than from
`container_interfaces_and_subnets` directly. The Interface::eq deduplication
could match interfaces with different UUIDs, causing bindings to reference
interface IDs not present in the list sent to the server. This resulted in
bindings falling back to interface_id: None, making services appear on all
interfaces in the topology.

Closes #409
…IDs (#404, #405, #407, #412, #415)

## Bug Fixes

### #404 - Host name edit works second time, not first
- Changed HostEditModalContent to use reactive $derived for formData
- Ensures form state updates when currentHost changes

### #405 - Reordering services cannot be saved
- Added position field to ServiceBase with database migration
- Added services sync to UpdateHostRequest with position tracking
- Implemented sync_services() method following interfaces pattern

### #407 - Transferring ports between services fails
- Fixed removeBinding to properly target source service
- Resolved stale closure issue in ServiceBindingManager

### #412 - Port binding dropdown validation broken
- Corrected filtering logic in getAvailableBindingOptions
- Fixed inverted availability check for port-interface combinations

### #415 - Discovery schedule changes not saved
- Fixed form state initialization in DaemonConfigModal
- Changed to $state with proper $effect for modal open/close

## Architecture: Client-Provided UUIDs

Consolidated API input types and implemented client-provided UUIDs for all
host children (interfaces, ports, services, bindings):

### Backend Changes
- Consolidated 6 input types into 4: InterfaceInput, PortInput, ServiceInput, BindingInput
- All input types now require client-provided id: Uuid
- Added services field to CreateHostRequest for single-step creation
- Changed UpdateHostRequest children from Option<Vec> to Vec (empty = no sync)
- Backend determines create vs update by checking if ID exists for host

### Frontend Changes
- Updated mutations to always send entity IDs
- Services now use uuidv4() instead of sentinel for new entities
- Simplified update mutation (no more cache lookups for new vs existing)

### Benefits
- Single-step host creation with services and bindings
- Binding IDs preserved across updates
- New entities can reference each other in same request
- Simpler, more predictable API behavior

Closes #404, Closes #405, Closes #407, Closes #412, Closes #415
…Query

- Merge discovery/sse.ts into discovery/queries.ts with DiscoverySSEManager
- Merge topology/store.ts and topology/sse.ts into topology/queries.ts
- Add TopologySSEManager for real-time updates via TanStack cache
- Move UI-only state (selectedTopologyId, selectedNode, selectedEdge, etc.) to queries.ts as Svelte stores
- Update all topology and discovery components to import from queries.ts
- Remove deprecated sse.ts and store.ts files
- Add schema.d.ts to .prettierignore (generated file)
…tional

Frontend fixes:
- Fix discovery card animation so only icon spins, not cancel label
- Sort services by position in HostEditor and HostCard
- Add services to TanStack Form tracking (defaultValues and reset) so
  form state syncs with formData
- Pass form prop to ServicesForm and call form.setFieldValue on reorder,
  matching the pattern used by InterfacesForm
- Update service positions on drag-and-drop reorder
- Fix interface form remounting on reorder by including index in key
- Remove local prop mutation in ListManager moveItemUp/moveItemDown that
  shadowed props in Svelte 5, breaking reactivity chain

Backend fixes:
- Remove position preservation in ServiceService.update() that was
  overwriting incoming position values with existing ones
- Add get_all_ordered method to ServiceService for position-aware queries
- Update load_children_for_host and load_children_for_hosts in HostService
  to fetch services ordered by position ASC
- Make position field optional in ServiceInput and InterfaceInput
- Add resolve_and_validate_input_positions() for automatic position handling
- On create with omitted positions: assign 0, 1, 2... in input order
- On update with omitted positions: preserve existing, append new to end
- Mixed explicit/omitted positions returns validation error

The root cause of reorder not persisting was ServiceService.update()
explicitly preserving the old position value, ignoring the new position
sent from the frontend. This was compounded by frontend issues where
services weren't tracked in form state and ListManager's local mutations
interfered with Svelte 5 reactivity.
…ion (#412)

The interface and port dropdowns in the port binding editor had a
circular validation dependency that prevented users from selecting
valid (interface, port) combinations when they needed to change both
values simultaneously.

Changes:
- Remove cross-field validation from interface dropdown - users can
  now freely select any interface
- Port dropdown validates against the currently selected interface
- Auto-select first valid port when interface changes and current
  port is invalid or empty
- Show "No available ports on this interface" error when no valid
  ports exist for selected interface
- Wire up ServiceConfigPanel to TanStack Form validation:
  - Service name field uses form.Field with required/max validators
  - Port bindings register hidden form fields that validate port_id
  - Form submission blocked when bindings have invalid port selection
- Add form prop to ServiceEditModal for standalone service editing
- Fix branching parent options not appearing in create modal by using
  local $state synced via form.store.subscribe (form.state.values is
  not tracked by Svelte 5's $derived)
- Fix delete not selecting next topology by capturing topology ID before
  async mutation (currentTopology becomes null after query refetch)
- Add RadioGroup form component for styled radio button groups
- Add creation mode toggle (Branch from existing / Start fresh) that
  controls whether parent_id is set
- Pre-select currently viewed topology as branching parent, or first
  available topology on the network
@mayanayza mayanayza merged commit ef66621 into main Jan 4, 2026
4 checks passed
mayanayza added a commit that referenced this pull request Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant