From 8414c26bd4b6c76c4d82450af5b22ce295298082 Mon Sep 17 00:00:00 2001 From: urania Date: Thu, 25 Jun 2026 23:11:32 +0200 Subject: [PATCH] fix: some improvement and docs --- db.sqlite | Bin 94208 -> 94208 bytes messages/en.json | 1545 +- src/hooks.server.ts | 4 +- .../blocks/sidebar/machines-nav.svelte | 8 +- .../components/blocks/terminal-dialog.svelte | 140 +- .../dashboard/activity-panel.svelte | 2 +- .../components/dashboard/cpu-heatmap.svelte | 4 +- .../components/dashboard/data-table.svelte | 8 +- src/lib/components/dashboard/format.ts | 14 + src/lib/components/dashboard/gpu-card.svelte | 74 + .../components/dashboard/storage-panel.svelte | 1 + .../components/dashboard/system-panel.svelte | 2 +- .../dashboard/temperature-panel.svelte | 15 +- src/lib/components/dashboard/types.ts | 2 +- .../components/ui/field/field-label.svelte | 2 +- src/lib/const/schema.ts | 18 +- src/lib/remotes/machines.remote.ts | 8 + src/lib/remotes/packages.remote.ts | 5 +- src/lib/remotes/system.remote.ts | 17 +- src/lib/remotes/terminal.remote.ts | 78 +- src/lib/remotes/users.remote.ts | 2 +- src/lib/remotes/utils.ts | 7 +- src/lib/server/db/custom-types.ts | 22 +- src/lib/server/nadir-agent/schema.d.ts | 13315 ++++++++-------- src/lib/server/terminal/session.ts | 18 +- src/lib/utils.ts | 15 +- src/routes/+error.svelte | 2 +- src/routes/+layout.svelte | 5 +- src/routes/+page.svelte | 458 +- src/routes/auth/forgot-password/+page.svelte | 8 +- src/routes/auth/reset-password/+page.svelte | 4 +- src/routes/auth/sign-in/+page.svelte | 6 +- src/routes/auth/sign-up/+page.svelte | 8 +- src/routes/dashboard/+page.svelte | 360 + src/routes/dashboard/[machineId]/+page.svelte | 155 +- .../[machineId]/networking/+page.svelte | 2 +- .../[machineId]/networking/dns/+page.svelte | 2 +- .../[machineId]/networking/hosts/+page.svelte | 18 +- .../networking/interfaces/+page.svelte | 112 +- .../networking/interfaces/[name]/+page.svelte | 51 +- .../interfaces/[name]/configure/+page.svelte | 19 +- .../networking/routes/+page.svelte | 6 +- .../[machineId]/packages/+page.svelte | 2 +- .../packages/installed/+page.svelte | 21 +- .../[machineId]/packages/updates/+page.svelte | 8 +- .../[machineId]/services/+page.svelte | 36 +- .../[machineId]/services/[name]/+page.svelte | 177 +- .../[machineId]/storage/+page.svelte | 2 +- .../[machineId]/storage/fstab/+page.svelte | 14 +- .../[machineId]/storage/mounts/+page.svelte | 48 +- .../dashboard/[machineId]/system/+page.svelte | 36 +- .../[machineId]/system/date-time/+page.svelte | 64 +- .../[machineId]/system/hostname/+page.svelte | 9 +- .../system/localization/+page.svelte | 17 +- .../[machineId]/system/nadir/+page.svelte | 74 +- .../[machineId]/system/power/+page.svelte | 35 +- .../dashboard/[machineId]/users/+page.svelte | 20 +- .../[machineId]/users/[username]/+page.svelte | 13 +- .../[machineId]/users/groups/+page.svelte | 23 +- .../users/groups/[group]/+page.svelte | 12 +- src/routes/docs/+layout.svelte | 115 + src/routes/docs/+page.svelte | 85 + src/routes/docs/architecture/+page.svelte | 133 + src/routes/docs/installation/+page.svelte | 122 + src/routes/docs/limitations/+page.svelte | 58 + src/routes/docs/security/+page.svelte | 89 + src/routes/install.sh/+server.ts | 91 +- src/routes/install.sh/install.sh.tmpl | 112 + 68 files changed, 9965 insertions(+), 7993 deletions(-) create mode 100644 src/lib/components/dashboard/gpu-card.svelte create mode 100644 src/routes/docs/+layout.svelte create mode 100644 src/routes/docs/+page.svelte create mode 100644 src/routes/docs/architecture/+page.svelte create mode 100644 src/routes/docs/installation/+page.svelte create mode 100644 src/routes/docs/limitations/+page.svelte create mode 100644 src/routes/docs/security/+page.svelte create mode 100644 src/routes/install.sh/install.sh.tmpl diff --git a/db.sqlite b/db.sqlite index c35d9d396463b1c3c85a9d65c523512f9843e45c..8b162dc97f02c0554fa119c88df16184a94686ef 100644 GIT binary patch delta 111 zcmZp8z}oPDb%HeG#ECM_j1wCZwk9wxv=`;x#lX%{W5v_K^_tUj_Q`SqdBrx(+9? zp$7rC9YFzK2baK00Wy~cLjjBwflmeqRT~6?4Lty1H)At3Ffn5-w@yg`Yyt#usermod -G on each user. Primary-group members (users whose primary gid is {gid}) appear below but cannot be removed from here.", - "groups_members_title": "Members", - "groups_nav_description": "Unix groups from /etc/group on this machine.", - "groups_nav_title": "Groups", - "groups_no_results": "No groups found.", - "groups_remove_member_aria": "Remove {name}", - "groups_no_supplementary_members": "No supplementary members.", - "groups_not_found": "Group not found: {name}", - "groups_primary_empty": "None.", - "groups_primary_label": "Primary ({count})", - "groups_search_placeholder": "Search name or gid…", - "groups_supplementary_label": "Supplementary ({count})", - "home": "Home", - "invalid_reset_link": "This link is invalid or has expired.", - "login": "Login", - "more": "More", - "login_social_description": "You have to login to use this platform. Use your favorite social or your credentials", - "login_with": "Login with {social}", - "logout": "Logout", - "machine_actions": "Server actions", - "machine_add": "Add server", - "machine_add_description": "Connect a new server to manage from this dashboard.", - "machine_address": "Address", - "machine_address_placeholder": "http://127.0.0.1:9999", - "machine_delete_confirm": "This permanently removes \"{name}\" from the database. The connected server is not touched.", - "machine_delete_title": "Delete server?", - "machine_edit": "Edit server", - "machine_edit_description": "Update the connection details for this server.", - "machine_name": "Name", - "machine_name_placeholder": "Production server", - "machine_none": "No servers yet.", - "machine_offline": "Offline", - "machine_offline_code": "Error 502", - "machine_offline_description": "Could not reach the nadir-agent at {address}. The host may be down, the agent stopped, or the address is wrong.", - "machine_offline_details": "Show error details", - "machine_offline_node_dest": "Agent", - "machine_offline_node_proxy": "Web UI", - "machine_offline_node_you": "You", - "machine_offline_status_connected": "CONNECTED", - "machine_offline_status_unreachable": "UNREACHABLE", - "machine_offline_title": "{name} is offline", - "machine_online": "Online", - "machine_refresh_health": "Refresh status", - "machine_reorder": "Reorder servers", - "machine_reorder_done": "Done reordering", - "machine_save": "Add server", - "machine_save_edit": "Save changes", - "machine_search_placeholder": "Search servers…", - "machine_token": "Token", - "machine_token_keep": "Leave blank to keep the current token.", - "machine_token_placeholder": "Agent bearer token", - "manual_entry_key": "Can't scan? Enter this key in your authenticator app manually:", - "name": "Name", - "nav_admin": "Admin", - "nav_admin_config": "Config", - "nav_admin_config_desc": "Application-wide configuration.", - "nav_admin_users": "Users", - "nav_admin_users_desc": "Manage user accounts, roles and access.", - "nav_dashboard_overview": "Overview", - "nav_dashboard_overview_desc": "System status at a glance.", - "nav_networking": "Networking", - "nav_networking_dns": "DNS", - "nav_networking_dns_desc": "Nameservers from /etc/resolv.conf.", - "nav_networking_hosts": "Hosts", - "nav_networking_hosts_desc": "Static host entries in /etc/hosts.", - "nav_networking_interfaces": "Interfaces", - "nav_networking_interfaces_desc": "Network interfaces and their addresses.", - "nav_networking_routes": "Routes", - "nav_networking_routes_desc": "Kernel routing table.", - "nav_packages": "Packages", - "nav_packages_installed": "Installed packages", - "nav_packages_installed_desc": "List, search, and remove installed software packages.", - "nav_packages_updates": "Available updates", - "nav_packages_updates_desc": "Check and install updates for existing packages.", - "nav_services": "Services", - "nav_services_desc": "Manage systemd service units.", - "nav_storage": "Storage", - "nav_storage_fstab": "Fstab", - "nav_storage_fstab_desc": "Persistent mount definitions in /etc/fstab.", - "nav_storage_mounts": "Mounts", - "nav_storage_mounts_desc": "Active filesystem mounts on this machine.", - "nav_system": "System", - "nav_system_datetime": "Date & Time", - "nav_system_datetime_desc": "Clock, timezone and time synchronisation.", - "nav_system_hostname": "Hostname", - "nav_system_hostname_desc": "Identify this machine on the network.", - "nav_system_localization": "Localization", - "nav_system_localization_desc": "Language, locale and region settings.", - "nav_system_nadir": "Nadir Agent", - "nav_system_nadir_desc": "Active modules and user permissions.", - "nav_system_power": "Power", - "nav_system_power_desc": "Reboot or power off the machine.", - "nav_system_terminal": "Terminal", - "nav_system_terminal_desc": "Open an interactive terminal session on this host.", - "nav_users_groups": "Groups", - "nav_users_groups_desc": "Unix groups from /etc/group.", - "nav_users_system_users": "System users", - "nav_users_system_users_desc": "PAM/Unix accounts on this machine.", - "networking_apply": "Apply", - "networking_apply_confirm_body": "The agent will activate this configuration and auto-revert after {seconds}s unless you confirm. Make sure you can still reach the host after applying.", - "networking_apply_confirm_title": "Apply with rollback?", - "networking_apply_done": "Configuration applied — waiting for confirmation.", - "networking_col_destination": "Destination", - "networking_col_gateway": "Gateway", - "networking_col_hostnames": "Hostnames", - "networking_col_interface": "Interface", - "networking_col_ip": "IP", - "networking_col_ipv4": "IPv4", - "networking_col_ipv6": "IPv6", - "networking_col_mac": "MAC", - "networking_col_metric": "Metric", - "networking_col_mtu": "MTU", - "networking_col_name": "Name", - "networking_col_source": "Source", - "networking_col_state": "State", - "networking_configure": "Configure", - "networking_configure_description": "Apply addressing, gateway, DNS and static routes. Changes auto-rollback after the configured timeout unless confirmed.", - "networking_confirm": "Confirm", - "networking_confirmed": "Change confirmed.", - "networking_details": "Details", - "networking_dns_add": "Add nameserver", - "networking_dns_description": "Read-only view of the system resolver. Configure per interface.", - "networking_dns_per_interface_hint": "DNS is set per interface; there is no standalone write endpoint.", - "networking_dns_placeholder": "1.1.1.1", - "networking_dns_section": "DNS", - "networking_dns_section_hint": "Resolvers for this interface (IPv4 or IPv6).", - "networking_dns_servers": "Nameservers", - "networking_host_add": "Add host", - "networking_host_add_description": "Map an IP to one or more hostnames. Multiple names separated by spaces.", - "networking_host_edit": "Edit host", - "networking_host_remove": "Remove host", - "networking_host_remove_description": "This will delete the entry from /etc/hosts.", - "networking_host_remove_title": "Remove {ip}?", - "networking_host_removed": "Host entry removed.", - "networking_host_saved": "Host entry saved.", - "networking_hosts_description": "Static name-to-address mappings in /etc/hosts.", - "networking_hosts_search_placeholder": "Search by IP or hostname...", - "networking_interfaces_description": "Network interfaces and their current addresses.", - "networking_interfaces_search_placeholder": "Search by name, MAC, address...", - "networking_ipv4_section": "IPv4", - "networking_ipv4_section_hint": "Static address or DHCP.", - "networking_ipv6_section": "IPv6", - "networking_ipv6_section_hint": "Auto (SLAAC), static, or ignore to disable.", - "networking_link_down": "Bring down", - "networking_link_down_done": "Interface {name} brought down.", - "networking_link_up": "Bring up", - "networking_link_up_done": "Interface {name} brought up.", - "networking_method_dhcp": "DHCP", - "networking_method_ignore": "Ignore (disable)", - "networking_method_slaac": "Auto (SLAAC)", - "networking_method_static": "Static", - "networking_no_dns": "No nameservers configured.", - "networking_no_hosts": "No host entries.", - "networking_no_interfaces": "No interfaces.", - "networking_no_routes": "No routes.", - "networking_pending_banner": "Pending change on {iface} — auto-rollback in {seconds}s.", - "networking_rollback": "Rollback", - "networking_rollback_seconds": "Rollback timeout (seconds)", - "networking_rollback_seconds_hint": "Auto-revert after this many seconds unless confirmed. Leave 0 for the agent default (60s).", - "networking_rolled_back": "Change rolled back.", - "networking_route_add": "Add route", - "networking_route_destination": "Destination (CIDR or 'default')", - "networking_route_gateway": "Next-hop gateway", - "networking_routes_description": "Kernel routing table.", - "networking_routes_search_placeholder": "Search by destination, gateway, interface...", - "networking_routes_section": "Static routes", - "networking_routes_section_hint": "Optional routes installed alongside this interface.", - "new_password": "New password", - "no_account": "No account yet?", - "optional": "Optional", - "or": "Or", - "packages_col_name": "Package", - "packages_col_version": "Version", - "packages_install": "Install Package", - "packages_install_button": "Install", - "packages_install_desc": "Enter the name of the package you want to install from the repositories.", - "packages_install_started": "Installing package {name}...", - "packages_install_success": "Package {name} installed successfully.", - "packages_manager_label": "Package Manager", - "packages_no_packages": "No packages found.", - "packages_no_updates": "No updates available.", - "packages_remove_confirm_desc": "This will uninstall {name} from the host. Any dependent packages might also be affected.", - "packages_remove_confirm_title": "Remove {name}?", - "packages_remove_started": "Removing package {name}...", - "packages_remove_success": "Package {name} removed successfully.", - "packages_search_placeholder": "Search packages by name...", - "packages_stream_connection_error": "Connection Error: {message}", - "packages_stream_error": "ERROR: {message}", - "packages_stream_failed": "Operation failed.", - "packages_terminal_desc": "Streaming output from the package manager.", - "packages_update_available": "Update available {from} > {to}", - "packages_update_single": "Update", - "packages_update_started": "Upgrading package {name}...", - "packages_update_success": "Package {name} upgraded successfully.", - "packages_upgrade_all": "Upgrade All", - "packages_upgrade_success": "Upgrade completed successfully.", - "pagination_next": "Next", - "pagination_page_of": "Page {page} of {pages}", - "pagination_previous": "Previous", - "password": "Password", - "password_hint": "At least 8 characters, mixing upper- and lower-case letters and a number.", - "prefix": "Prefix", - "remember_me": "Remember me", - "reset_link_sent": "If an account exists for that email, a reset link is on its way.", - "reset_password_action": "Update password", - "reset_password_description": "Choose a strong password you don't use anywhere else.", - "reset_password_title": "Set a new password", - "save": "Save", - "saved": "Saved", - "scan_qr": "Add this key to your authenticator app, then enter the generated code below.", - "send_reset_link": "Send reset link", - "seo_desc_admin_config": "Application-wide configuration settings.", - "seo_desc_admin_users": "Manage user accounts, roles and access.", - "seo_desc_auth_2fa": "Verify your identity with a two-factor authentication code.", - "seo_desc_auth_forgot_password": "Reset your password via email.", - "seo_desc_auth_reset_password": "Choose a new password for your account.", - "seo_desc_auth_setup_2fa": "Add an extra layer of security with two-factor authentication.", - "seo_desc_auth_sign_in": "Sign in to manage your servers.", - "seo_desc_auth_sign_up": "Create an account to get started.", - "seo_desc_dashboard": "Server overview and health at a glance.", - "seo_desc_machine_detail": "System status, performance metrics and key information for this machine.", - "seo_desc_networking": "Network interfaces, routes, hosts and DNS configuration.", - "seo_desc_networking_dns": "Nameserver configuration from /etc/resolv.conf.", - "seo_desc_networking_hosts": "Static host entries in /etc/hosts.", - "seo_desc_networking_interfaces": "Network interfaces and their addresses.", - "seo_desc_networking_routes": "Kernel routing table.", - "seo_desc_packages": "Manage installed packages and system updates.", - "seo_desc_packages_installed": "List, search, and remove installed software packages.", - "seo_desc_packages_updates": "Check and install available package updates.", - "seo_desc_root": "Web-based server management dashboard.", - "seo_desc_services": "List, filter, and manage systemd service units.", - "seo_desc_services_detail": "Manage and monitor a systemd service unit.", - "seo_desc_storage": "Filesystem mounts and fstab configuration.", - "seo_desc_storage_fstab": "Persistent mount definitions in /etc/fstab.", - "seo_desc_storage_mounts": "Active filesystem mounts on this machine.", - "seo_desc_system": "Date and time, hostname, localization and power controls.", - "seo_desc_system_datetime": "Clock, timezone and time synchronisation settings.", - "seo_desc_system_hostname": "View and change the machine hostname.", - "seo_desc_system_localization": "Language, locale and region settings.", - "seo_desc_system_nadir": "View active modules and permission matrix for the connected Nadir agent.", - "seo_desc_system_power": "Reboot or power off the machine.", - "seo_desc_users": "PAM/Unix user accounts on this machine.", - "seo_desc_users_detail": "View and manage a PAM/Unix user account.", - "seo_desc_users_groups": "Unix groups from /etc/group on this machine.", - "seo_desc_users_groups_detail": "View and manage a Unix group.", - "seo_title_admin_config": "Config", - "seo_title_admin_users": "Users", - "seo_title_auth_2fa": "Two-factor authentication", - "seo_title_auth_forgot_password": "Forgot password", - "seo_title_auth_reset_password": "Reset password", - "seo_title_auth_setup_2fa": "Set up two-factor authentication", - "seo_title_auth_sign_in": "Sign in", - "seo_title_auth_sign_up": "Sign up", - "seo_title_dashboard": "Dashboard", - "seo_title_networking": "Networking", - "seo_title_networking_dns": "DNS", - "seo_title_networking_hosts": "Hosts", - "seo_title_networking_interfaces": "Interfaces", - "seo_title_networking_routes": "Routes", - "seo_title_packages": "Packages", - "seo_title_packages_installed": "Installed packages", - "seo_title_packages_updates": "Available updates", - "seo_title_root": "Home", - "seo_title_services": "Services", - "seo_title_storage": "Storage", - "seo_title_storage_fstab": "Fstab", - "seo_title_storage_mounts": "Mounts", - "seo_title_system": "System", - "seo_title_system_datetime": "Date and time", - "seo_title_system_hostname": "Hostname", - "seo_title_system_localization": "Localization", - "seo_title_system_nadir": "Nadir Agent", - "seo_title_system_power": "Power", - "seo_title_users": "System users", - "seo_title_users_groups": "Groups", - "services_action_disable": "Disable", - "services_action_disabled": "Service {name} disabled successfully.", - "services_action_enable": "Enable", - "services_action_enabled": "Service {name} enabled successfully.", - "services_action_restart": "Restart", - "services_action_restarted": "Service {name} restarted successfully.", - "services_action_start": "Start", - "services_action_started": "Service {name} started successfully.", - "services_action_stop": "Stop", - "services_action_stopped": "Service {name} stopped successfully.", - "services_active_filter": "Active State", - "services_col_active": "Active State", - "services_col_description": "Description", - "services_col_load": "Load State", - "services_col_sub": "Sub State", - "services_col_unit": "Unit", - "services_description": "List, filter, and manage systemd service units.", - "services_details_active_state": "Active State", - "services_details_load_state": "Load State", - "services_details_sub_state": "Sub State", - "services_details_title": "Service Details", - "services_details_unit_file_state": "Startup Type", - "services_filter_active": "Active", - "services_filter_dead": "Dead", - "services_filter_error": "Error", - "services_filter_exited": "Exited", - "services_filter_failed": "Failed", - "services_filter_inactive": "Inactive", - "services_filter_loaded": "Loaded", - "services_filter_masked": "Masked", - "services_filter_not_found": "Not Found", - "services_filter_other": "Other", - "services_filter_running": "Running", - "services_filter_title": "Filter Services", - "services_load_filter": "Load State", - "services_logs_autoscroll": "Autoscroll", - "services_logs_clear": "Clear", - "services_logs_empty": "No log entries.", - "services_logs_lines": "Lines", - "services_logs_live": "Live", - "services_logs_live_streaming": "Streaming", - "services_logs_priority": "Max priority", - "services_logs_search_placeholder": "Filter log lines...", - "services_logs_since": "Since", - "services_logs_since_15m": "Last 15 minutes", - "services_logs_since_1h": "Last hour", - "services_logs_since_6h": "Last 6 hours", - "services_logs_since_all": "All", - "services_logs_since_today": "Today", - "services_logs_since_yesterday": "Since yesterday", - "services_logs_title": "Logs", - "services_no_services": "No services found.", - "services_search_placeholder": "Search service units...", - "services_sub_filter": "Sub State", - "services_title": "Services", - "settings": "Settings", - "setup_2fa_description": "Add an extra layer of security to your account.", - "setup_2fa_title": "Set up two-factor authentication", - "sign_up": "Sign up", - "sign_up_description": "Sign up with your email and a username.", - "sign_up_title": "Create your account", - "storage_badge_fstab": "fstab", - "storage_col_device": "Device", - "storage_col_dump": "Dump", - "storage_col_fstype": "Type", - "storage_col_mountpoint": "Mount point", - "storage_col_options": "Options", - "storage_col_pass": "Pass", - "storage_filter_show_pseudo": "Show pseudo filesystems", - "storage_filter_show_pseudo_hint": "proc, sysfs, tmpfs, cgroup, …", - "storage_fstab_description": "Persistent mount definitions from /etc/fstab.", - "storage_mount_add": "Add mount", - "storage_mount_add_description": "Appends an /etc/fstab entry and mounts it. If the mount fails the entry is rolled back.", - "storage_mount_added": "Filesystem mounted", - "storage_mount_remove": "Unmount", - "storage_mount_remove_description": "Unmounts the filesystem and removes its /etc/fstab entry. This cannot be undone.", - "storage_mount_remove_title": "Unmount {mountpoint}?", - "storage_mount_removed": "Filesystem unmounted", - "storage_mounts_description": "Active mounts from the kernel mount table.", - "storage_mounts_search_placeholder": "Search by device, mount point or type…", - "storage_no_fstab": "No fstab entries.", - "storage_no_mounts": "No mounts found.", - "system_hostname_current": "Current hostname", - "system_hostname_invalid": "Hostname is invalid", - "system_locale_generate": "Generate new locale", - "system_locale_generate_button": "Generate", - "system_locale_generate_desc": "Install a new locale on the host. On Debian/Ubuntu/Arch this uncomments the entry in /etc/locale.gen and runs locale-gen; on RHEL/Fedora it uses localedef.", - "system_locale_generate_invalid": "Use the form xx_XX.UTF-8 (e.g. fr_FR.UTF-8)", - "system_locale_generate_placeholder": "e.g. ja_JP.UTF-8", - "system_locale_keymap": "Console keymap", - "system_locale_lang": "System locale (LANG)", - "system_locale_language": "Fallback language (LANGUAGE)", - "system_locale_language_add": "Add language", - "system_locale_language_button": "Save", - "system_locale_language_desc": "Set the fallback language priority list for system messages and translations (optional).", - "system_locale_language_empty": "No fallback language set.", - "system_locale_no_keymap_found": "No keymap found.", - "system_locale_no_locale_found": "No locale found.", - "system_locale_search_keymap_placeholder": "Search keymap…", - "system_locale_search_locale_placeholder": "Search locale…", - "system_locale_x11": "X11 layout", - "system_power_confirm_description": "The machine will be unreachable while it shuts down. This cannot be undone from here.", - "system_power_confirm_poweroff_title": "Power off this machine?", - "system_power_confirm_reboot_title": "Reboot this machine?", - "system_power_poweroff": "Power off", - "system_power_reboot": "Reboot", - "system_time_current": "Current time", - "system_time_manual": "Manual time", - "system_time_manual_hint": "Set the system clock to a specific RFC3339 time. Available only when NTP is off.", - "system_time_no_timezone_found": "No timezone found.", - "system_time_ntp": "Network time (NTP)", - "system_time_ntp_hint": "Automatically synchronize the clock with NTP servers.", - "system_time_ntp_not_synced": "Not synchronized", - "system_time_ntp_synced": "Synchronized", - "system_time_search_timezone_placeholder": "Search timezone…", - "system_time_timezone": "Timezone", - "syslog_alert": "1 alert", - "syslog_crit": "2 crit", - "syslog_debug": "7 debug", - "syslog_emerg": "0 emerg", - "syslog_err": "3 err", - "syslog_info": "6 info", - "syslog_notice": "5 notice", - "syslog_warning": "4 warning", - "terms_notice": "By clicking continue, you agree to our Terms of Service and Privacy Policy.", + "$schema": "https://inlang.com/schema/inlang-message-format", + "account": "Account", + "agent_update_failed": "Agent update did not complete - check `nadir logs` on the host", + "agent_update_started": "Updating agent... ({from} → {to})", + "agent_update_success": "Agent updated to {version}", + "agent_updates": "Agent outdated", + "already_have_account": "Already have an account?", + "appname": "NadiЯ", + "back_to_login": "Back to login", + "backup_code": "Backup code", + "backup_codes_notice": "Store these somewhere safe. Each code works once if you lose your device.", + "backup_codes_title": "Save your backup codes", + "cancel": "Cancel", + "check_your_email": "Check your email", + "code": "Code", + "confirm_password": "Confirm password", + "continue_action": "Continue", + "copied": "Copied", + "copy": "Copy", + "copy_failed": "Failed to copy", + "create_account": "Create account", + "dashboard": "Dashboard", + "dashboard_architecture": "Architecture", + "dashboard_auto_refresh": "Auto-refresh", + "dashboard_ascending": "Ascending", + "dashboard_clock": "Clock", + "dashboard_col_actions": "Actions", + "dashboard_col_free": "Free", + "dashboard_col_ip": "IP", + "dashboard_col_method": "Method", + "dashboard_col_mount": "Mount", + "dashboard_col_name": "Name", + "dashboard_col_path": "Path", + "dashboard_col_sensor": "Sensor", + "dashboard_col_size": "Size", + "dashboard_col_status": "Status", + "dashboard_col_temp": "Temp", + "dashboard_col_time": "Time", + "dashboard_col_usage": "Usage", + "dashboard_col_user": "User", + "dashboard_cpu": "CPU", + "dashboard_cpu_detail": "{cores} cores · {current} ({min}–{max})", + "dashboard_descending": "Descending", + "dashboard_dns": "DNS", + "dashboard_filter_display": "Display", + "dashboard_filter_title": "Filter", + "dashboard_free_of": "{free} free of {total}", + "dashboard_gpu": "GPU", + "dashboard_gpu_compute": "Compute", + "dashboard_gpu_vram": "VRAM", + "dashboard_hardware_clock": "Hardware clock", + "dashboard_interval_10s": "Every 10s", + "dashboard_interval_30s": "Every 30s", + "dashboard_interval_5s": "Every 5s", + "dashboard_interval_custom": "Custom", + "dashboard_interval_second": "Every second", + "dashboard_kernel": "Kernel", + "dashboard_keymap": "Keymap", + "dashboard_language": "Language", + "dashboard_load_average": "Load Average", + "dashboard_load_detail": "{loadPct}% of {cores}c · 5m {load5} · 15m {load15}", + "dashboard_local_time": "Local time", + "dashboard_locale": "Locale", + "dashboard_logical_cores": "{cores} logical cores", + "dashboard_machines_description": "Manage all servers connected to this dashboard.", + "dashboard_memory": "Memory", + "dashboard_nameserver": "Nameserver", + "dashboard_network": "Network", + "dashboard_next": "Next", + "dashboard_none": "none", + "dashboard_not_synced": "Not synced", + "dashboard_nothing_to_show": "Nothing to show.", + "dashboard_os": "OS", + "dashboard_packages": "Packages", + "dashboard_page_of": "Page {page} of {total}", + "dashboard_pagination_info": "{start}–{end} of {total}", + "dashboard_pause": "Pause auto-refresh", + "dashboard_prev": "Prev", + "dashboard_recent_activity": "Recent activity", + "dashboard_refresh": "Refresh", + "dashboard_resume": "Resume auto-refresh", + "dashboard_rows_per_page": "Rows per page", + "dashboard_search_placeholder": "Search", + "dashboard_since": "since {bootTime}", + "dashboard_status_down": "down", + "dashboard_status_up": "up", + "dashboard_storage": "Storage", + "dashboard_swap": "Swap {swapPct}%", + "dashboard_synchronized": "Synchronized", + "dashboard_syncing": "Syncing…", + "dashboard_system": "System", + "dashboard_temperatures": "Temperatures", + "dashboard_time": "Time", + "dashboard_timezone": "Timezone", + "dashboard_up_to_date": "up to date", + "dashboard_updates": "{count} updates", + "dashboard_uptime": "Uptime", + "dashboard_used_percent": "{used}% used", + "dashboard_utc": "UTC", + "delete": "Delete", + "download": "Download", + "edit": "Edit", + "email": "Email", + "email_complete_body": "Your account has been created. Choose a password to finish setting it up.", + "email_complete_button": "Set password", + "email_complete_heading": "Complete your registration", + "email_complete_subject": "Complete your registration", + "email_greeting": "Hi {name},", + "email_link_fallback": "If the button doesn't work, copy and paste this link into your browser:", + "email_otp_body": "Use this code to finish signing in:", + "email_otp_heading": "Your verification code", + "email_otp_ignore": "If you didn't try to sign in, you can safely ignore this email.", + "email_otp_subject": "Your verification code", + "email_placeholder": "admin@example.com", + "email_reset_body": "We received a request to reset the password for your account. Click the button below to choose a new password.", + "email_reset_button": "Reset password", + "email_reset_heading": "Reset your password", + "email_reset_ignore": "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.", + "email_reset_subject": "Reset your password", + "email_verify_body": "Click the button below to verify your email address and activate your account.", + "email_verify_button": "Verify email", + "email_verify_heading": "Verify your email", + "email_verify_ignore": "If you didn't create an account, you can safely ignore this email.", + "email_verify_subject": "Verify your email address", + "enter_password_to_continue": "Confirm your password to continue", + "error_action_back": "Go back", + "error_action_home": "Go home", + "error_action_retry": "Try again", + "error_code_label": "Error {status}", + "error_details": "Details", + "error_generic_description": "Something went wrong while loading this page.", + "error_generic_title": "Unexpected error", + "error_not_found_description": "The page or resource you are looking for could not be found. It may have been removed, renamed, or never existed.", + "error_not_found_title": "Not found", + "error_unauthorized_description": "You do not have access to this resource. Try signing in again or contact your administrator.", + "error_unauthorized_title": "Access denied", + "errors_address_invalid": "Enter a valid URL, e.g. http://127.0.0.1:9999", + "errors_email_invalid": "Enter a valid email address", + "errors_generic": "An error occurred during this operation, please review Nadir Logs for more information.", + "errors_invalid_code": "Invalid or expired code, try again.", + "errors_non_empty": "This field is required", + "errors_not_found": "This item has not been found", + "errors_password_too_short": "Password must be at least {min} characters", + "errors_password_weak": "Use upper- and lower-case letters and at least one number.", + "errors_passwords_no_match": "Passwords do not match", + "errors_unauthenticated": "Unauthenticated", + "errors_username_too_short": "Username must be at least {min} characters", + "errors_wrong_credentials": "Wrong credentials, try again.", + "finish": "Finish", + "forbidden": "You are not allowed to this operation.", + "forgot_password": "Forgot your password?", + "forgot_password_description": "Enter your email and we'll send you a reset link.", + "forgot_password_title": "Forgot your password?", + "groups": "Groups", + "groups_add": "Add group", + "groups_add_member": "Add member", + "groups_add_member_action": "add", + "groups_add_member_description": "Adds this group to the user's supplementary set.", + "groups_add_member_no_results": "No matching users.", + "groups_add_member_search_placeholder": "Search user…", + "groups_add_member_title": "Add member to {name}", + "groups_col_gid": "GID", + "groups_col_members": "Members", + "groups_gid_optional": "GID (optional)", + "groups_create_description": "Adds a Unix group via groupadd.", + "groups_create_field_system": "System group", + "groups_create_title": "Create group", + "groups_created": "Group created", + "groups_delete_description": "Runs groupdel. Fails if it is the primary group of any existing user.", + "groups_delete_title": "Delete {name}?", + "groups_deleted": "Group deleted", + "groups_filter_show_system": "Show system groups", + "groups_filter_system_gid_hint": "(gid < 1000)", + "groups_member_added": "Added {username}", + "groups_member_removed": "Removed {username}", + "groups_members_description": "Supplementary members are managed via usermod -G on each user. Primary-group members (users whose primary gid is {gid}) appear below but cannot be removed from here.", + "groups_members_title": "Members", + "groups_nav_description": "Unix groups from /etc/group on this machine.", + "groups_nav_title": "Groups", + "groups_no_results": "No groups found.", + "groups_remove_member_aria": "Remove {name}", + "groups_no_supplementary_members": "No supplementary members.", + "groups_not_found": "Group not found: {name}", + "groups_primary_empty": "None.", + "groups_primary_label": "Primary ({count})", + "groups_search_placeholder": "Search name or gid…", + "groups_supplementary_label": "Supplementary ({count})", + "home": "Home", + "invalid_reset_link": "This link is invalid or has expired.", + "landing_cta_desc": "Deploy your own instance and manage your servers from any device.", + "landing_cta_title": "Ready to take control?", + "landing_features_dashboard": "Live performance metrics", + "landing_features_dashboard_desc": "Inspect real-time CPU performance heatmaps, memory consumption metrics, network bandwidth usage, and storage capacity graphs.", + "landing_features_networking": "Network administration", + "landing_features_networking_desc": "Configure network settings dynamically. Review interfaces, inspect routing tables, modify local hosts files, and configure system DNS resolution.", + "landing_features_services": "Systemd service control", + "landing_features_services_desc": "Control systemd services directly from the browser. Start, stop, restart units, toggle boot behavior, and follow log streams.", + "landing_features_storage": "Filesystem and storage", + "landing_features_storage_desc": "Monitor storage devices and filesystems. Mount and unmount storage volumes, view disk partitions, and configure fstab details.", + "landing_features_terminal": "Interactive terminal", + "landing_features_terminal_desc": "Open secure terminal windows connected directly to target hosts. Features terminal multiplexing, size auto-fitting, and persistent sessions.", + "landing_features_title": "Complete server control", + "landing_features_desc": "No complex configuration files or rigid wrappers. Nadir exposes standard system abstractions directly, letting you perform everyday administration actions natively.", + "landing_features_users": "User and group management", + "landing_features_users_desc": "Manage system user accounts and group assignments. Edit user privileges, view memberships, and authenticate via secure PAM rules.", + "landing_footer_docs": "Documentation", + "landing_footer_gitea": "GiTea", + "landing_footer_subtitle": "Self-hosted, open-source server management.", + "landing_hero_cta_github": "View source", + "landing_hero_cta_start": "Get started", + "landing_hero_title": "Modern server administration, unified.", + "landing_hero_tagline": "An open-source console and lightweight agent built to inspect, manage, and secure your Linux hosts from any browser. Simple architecture, complete control.", + "landing_arch_title": "Dual architecture, simple deployment", + "landing_arch_desc": "Nadir separates your presentation layer from target host runtimes. The frontend operates as a stateless console, and the lightweight Go agent manages your systems directly.", + "landing_arch_frontend_title": "Nadir Web UI", + "landing_arch_frontend_desc": "The central operations dashboard. Served as a stateless Docker container. Sign in, manage server associations, and monitor all nodes.", + "landing_arch_backend_title": "Nadir Agent", + "landing_arch_backend_desc": "The per-host service daemon. Lightweight binary compiled in Go. Runs as root under PAM authentication, providing local system access.", + "landing_how_title": "Deploy in under two minutes", + "landing_how_desc": "Follow these simple steps to run the Web UI and link your first target host.", + "landing_how_step1_title": "Spin up the Web UI", + "landing_how_step1_desc": "Deploy the stateless web dashboard using Docker. This interface will coordinate your registered agents. Access the web setup at port 3000 to configure your dashboard admin account.", + "landing_how_step2_title": "Bootstrap the target server", + "landing_how_step2_desc": "Run the installation script on the host machine you want to manage. The script automatically detects the host architecture (amd64/arm64), fetches the latest tagged binary release from Gitea, verifies SHA-256 integrity, configures a systemd unit, and installs a PAM configuration file.", + "landing_security_note": "Security note: The bootstrap installer requires root privileges. It generates a self-signed TLS certificate automatically if secure TLS is configured.", + "landing_security_architecture_title": "Deployment security and architecture", + "landing_security_architecture_desc": "Because the nadir-agent operates with root privileges, it should never be exposed directly to the public internet. By default, it binds to localhost. It is designed to be co-located on the same server as the Web UI communicating over loopback, or securely accessed across nodes over a private VPN (such as WireGuard, Tailscale, or Netbird). While agent installations support generating self-signed TLS certificates automatically, operating within a trusted private network boundary remains the recommended deployment model.", + "landing_how_step3_title": "Link in the dashboard", + "landing_how_step3_desc": "When the agent installation completes, it outputs a dashboard association token. Copy this Bearer token, log into the Web UI dashboard, click \"Add Machine,\" enter the IP/address and token, and start administering your server.", + "landing_screenshot_dashboard": "Machine dashboard with real-time performance metrics, CPU heatmap, memory usage, and network activity.", + "landing_screenshot_hero_label": "Dashboard overview with real-time metrics", + "landing_screenshot_machines": "Multi-server overview showing all connected machines with health status.", + "landing_screenshot_services": "Service management table with start, stop, and restart controls for systemd units.", + "landing_screenshots_title": "See it in action", + "landing_start_code": "bun install && cp .env.example .env && bun run db:push && bun run dev", + "landing_tech_linux": "Linux", + "landing_tech_opensource": "Open Source", + "landing_tech_selfhosted": "Self-Hosted", + "landing_start_desc": "Prerequisites: Bun and a reachable nadir-agent instance.", + "landing_start_title": "Get started in minutes", + "landing_stats_linux": "Linux-native", + "landing_stats_opensource": "Open source", + "landing_stats_setup": "One-command setup", + "login": "Login", + "more": "More", + "login_social_description": "You have to login to use this platform. Use your favorite social or your credentials", + "login_with": "Login with {social}", + "logout": "Logout", + "machine_actions": "Server actions", + "machine_add": "Add server", + "machine_add_description": "Connect a new server to manage from this dashboard.", + "machine_address": "Address", + "machine_address_placeholder": "http://127.0.0.1:9999", + "machine_delete_confirm": "This permanently removes \"{name}\" from the database. The connected server is not touched.", + "machine_delete_title": "Delete server?", + "machine_edit": "Edit server", + "machine_edit_description": "Update the connection details for this server.", + "machine_name": "Name", + "machine_name_placeholder": "Production server", + "machine_none": "No servers yet.", + "machine_offline": "Offline", + "machine_offline_code": "Error 502", + "machine_offline_description": "Could not reach the nadir-agent at {address}. The host may be down, the agent stopped, or the address is wrong.", + "machine_offline_details": "Show error details", + "machine_offline_node_dest": "Agent", + "machine_offline_node_proxy": "Web UI", + "machine_offline_node_you": "You", + "machine_offline_status_connected": "CONNECTED", + "machine_offline_status_unreachable": "UNREACHABLE", + "machine_offline_title": "{name} is offline", + "machine_online": "Online", + "machine_refresh_health": "Refresh status", + "machine_reorder": "Reorder servers", + "machine_reorder_done": "Done reordering", + "machine_save": "Add server", + "machine_save_edit": "Save changes", + "machine_search_placeholder": "Search servers…", + "machine_token": "Token", + "machine_token_keep": "Leave blank to keep the current token.", + "machine_token_placeholder": "Agent bearer token", + "manual_entry_key": "Can't scan? Enter this key in your authenticator app manually:", + "name": "Name", + "nav_admin": "Admin", + "nav_admin_config": "Config", + "nav_admin_config_desc": "Application-wide configuration.", + "nav_admin_users": "Users", + "nav_admin_users_desc": "Manage user accounts, roles and access.", + "nav_dashboard_overview": "Overview", + "nav_dashboard_overview_desc": "System status at a glance.", + "nav_networking": "Networking", + "nav_networking_dns": "DNS", + "nav_networking_dns_desc": "Nameservers from /etc/resolv.conf.", + "nav_networking_hosts": "Hosts", + "nav_networking_hosts_desc": "Static host entries in /etc/hosts.", + "nav_networking_interfaces": "Interfaces", + "nav_networking_interfaces_desc": "Network interfaces and their addresses.", + "nav_networking_routes": "Routes", + "nav_networking_routes_desc": "Kernel routing table.", + "nav_packages": "Packages", + "nav_packages_installed": "Installed packages", + "nav_packages_installed_desc": "List, search, and remove installed software packages.", + "nav_packages_updates": "Available updates", + "nav_packages_updates_desc": "Check and install updates for existing packages.", + "nav_services": "Services", + "nav_services_desc": "Manage systemd service units.", + "nav_storage": "Storage", + "nav_storage_fstab": "Fstab", + "nav_storage_fstab_desc": "Persistent mount definitions in /etc/fstab.", + "nav_storage_mounts": "Mounts", + "nav_storage_mounts_desc": "Active filesystem mounts on this machine.", + "nav_system": "System", + "nav_system_datetime": "Date & Time", + "nav_system_datetime_desc": "Clock, timezone and time synchronisation.", + "nav_system_hostname": "Hostname", + "nav_system_hostname_desc": "Identify this machine on the network.", + "nav_system_localization": "Localization", + "nav_system_localization_desc": "Language, locale and region settings.", + "nav_system_nadir": "Nadir Agent", + "nav_system_nadir_desc": "Active modules and user permissions.", + "nav_system_power": "Power", + "nav_system_power_desc": "Reboot or power off the machine.", + "nav_system_terminal": "Terminal", + "nav_system_terminal_desc": "Open an interactive terminal session on this host.", + "nav_users_groups": "Groups", + "nav_users_groups_desc": "Unix groups from /etc/group.", + "nav_users_system_users": "System users", + "nav_users_system_users_desc": "PAM/Unix accounts on this machine.", + "networking_apply": "Apply", + "networking_apply_confirm_body": "The agent will activate this configuration and auto-revert after {seconds}s unless you confirm. Make sure you can still reach the host after applying.", + "networking_apply_confirm_title": "Apply with rollback?", + "networking_apply_done": "Configuration applied — waiting for confirmation.", + "networking_col_destination": "Destination", + "networking_col_gateway": "Gateway", + "networking_col_hostnames": "Hostnames", + "networking_col_interface": "Interface", + "networking_col_ip": "IP", + "networking_col_ipv4": "IPv4", + "networking_col_ipv6": "IPv6", + "networking_col_mac": "MAC", + "networking_col_metric": "Metric", + "networking_col_mtu": "MTU", + "networking_col_name": "Name", + "networking_col_source": "Source", + "networking_col_state": "State", + "networking_configure": "Configure", + "networking_configure_description": "Apply addressing, gateway, DNS and static routes. Changes auto-rollback after the configured timeout unless confirmed.", + "networking_confirm": "Confirm", + "networking_confirmed": "Change confirmed.", + "networking_details": "Details", + "networking_dns_add": "Add nameserver", + "networking_dns_description": "Read-only view of the system resolver. Configure per interface.", + "networking_dns_per_interface_hint": "DNS is set per interface; there is no standalone write endpoint.", + "networking_dns_placeholder": "1.1.1.1", + "networking_dns_section": "DNS", + "networking_dns_section_hint": "Resolvers for this interface (IPv4 or IPv6).", + "networking_dns_servers": "Nameservers", + "networking_host_add": "Add host", + "networking_host_add_description": "Map an IP to one or more hostnames. Multiple names separated by spaces.", + "networking_host_edit": "Edit host", + "networking_host_remove": "Remove host", + "networking_host_remove_description": "This will delete the entry from /etc/hosts.", + "networking_host_remove_title": "Remove {ip}?", + "networking_host_removed": "Host entry removed.", + "networking_host_saved": "Host entry saved.", + "networking_hosts_description": "Static name-to-address mappings in /etc/hosts.", + "networking_hosts_search_placeholder": "Search by IP or hostname...", + "networking_interfaces_description": "Network interfaces and their current addresses.", + "networking_interfaces_search_placeholder": "Search by name, MAC, address...", + "networking_ipv4_section": "IPv4", + "networking_ipv4_section_hint": "Static address or DHCP.", + "networking_ipv6_section": "IPv6", + "networking_ipv6_section_hint": "Auto (SLAAC), static, or ignore to disable.", + "networking_link_down": "Bring down", + "networking_link_down_done": "Interface {name} brought down.", + "networking_link_up": "Bring up", + "networking_link_up_done": "Interface {name} brought up.", + "networking_method_dhcp": "DHCP", + "networking_method_ignore": "Ignore (disable)", + "networking_method_slaac": "Auto (SLAAC)", + "networking_method_static": "Static", + "networking_no_dns": "No nameservers configured.", + "networking_no_hosts": "No host entries.", + "networking_no_interfaces": "No interfaces.", + "networking_no_routes": "No routes.", + "networking_pending_banner": "Pending change on {iface} — auto-rollback in {seconds}s.", + "networking_rollback": "Rollback", + "networking_rollback_seconds": "Rollback timeout (seconds)", + "networking_rollback_seconds_hint": "Auto-revert after this many seconds unless confirmed. Leave 0 for the agent default (60s).", + "networking_rolled_back": "Change rolled back.", + "networking_route_add": "Add route", + "networking_route_destination": "Destination (CIDR or 'default')", + "networking_route_gateway": "Next-hop gateway", + "networking_routes_description": "Kernel routing table.", + "networking_routes_search_placeholder": "Search by destination, gateway, interface...", + "networking_routes_section": "Static routes", + "networking_routes_section_hint": "Optional routes installed alongside this interface.", + "new_password": "New password", + "no_account": "No account yet?", + "optional": "Optional", + "or": "Or", + "packages_col_name": "Package", + "packages_col_version": "Version", + "packages_install": "Install Package", + "packages_install_button": "Install", + "packages_install_desc": "Enter the name of the package you want to install from the repositories.", + "packages_install_started": "Installing package {name}...", + "packages_install_success": "Package {name} installed successfully.", + "packages_manager_label": "Package Manager", + "packages_no_packages": "No packages found.", + "packages_no_updates": "No updates available.", + "packages_remove_confirm_desc": "This will uninstall {name} from the host. Any dependent packages might also be affected.", + "packages_remove_confirm_title": "Remove {name}?", + "packages_remove_started": "Removing package {name}...", + "packages_remove_success": "Package {name} removed successfully.", + "packages_search_placeholder": "Search packages by name...", + "packages_stream_connection_error": "Connection Error: {message}", + "packages_stream_error": "ERROR: {message}", + "packages_stream_failed": "Operation failed.", + "packages_terminal_desc": "Streaming output from the package manager.", + "packages_update_available": "Update available {from} > {to}", + "packages_update_single": "Update", + "packages_update_started": "Upgrading package {name}...", + "packages_update_success": "Package {name} upgraded successfully.", + "packages_upgrade_all": "Upgrade All", + "packages_upgrade_success": "Upgrade completed successfully.", + "pagination_next": "Next", + "pagination_page_of": "Page {page} of {pages}", + "pagination_previous": "Previous", + "password": "Password", + "password_hint": "At least 8 characters, mixing upper- and lower-case letters and a number.", + "prefix": "Prefix", + "remember_me": "Remember me", + "reset_link_sent": "If an account exists for that email, a reset link is on its way.", + "reset_password_action": "Update password", + "reset_password_description": "Choose a strong password you don't use anywhere else.", + "reset_password_title": "Set a new password", + "save": "Save", + "saved": "Saved", + "scan_qr": "Add this key to your authenticator app, then enter the generated code below.", + "send_reset_link": "Send reset link", + "seo_desc_admin_config": "Application-wide configuration settings.", + "seo_desc_admin_users": "Manage user accounts, roles and access.", + "seo_desc_auth_2fa": "Verify your identity with a two-factor authentication code.", + "seo_desc_auth_forgot_password": "Reset your password via email.", + "seo_desc_auth_reset_password": "Choose a new password for your account.", + "seo_desc_auth_setup_2fa": "Add an extra layer of security with two-factor authentication.", + "seo_desc_auth_sign_in": "Sign in to manage your servers.", + "seo_desc_auth_sign_up": "Create an account to get started.", + "seo_desc_dashboard": "Server overview and health at a glance.", + "seo_desc_machine_detail": "System status, performance metrics and key information for this machine.", + "seo_desc_networking": "Network interfaces, routes, hosts and DNS configuration.", + "seo_desc_networking_dns": "Nameserver configuration from /etc/resolv.conf.", + "seo_desc_networking_hosts": "Static host entries in /etc/hosts.", + "seo_desc_networking_interfaces": "Network interfaces and their addresses.", + "seo_desc_networking_routes": "Kernel routing table.", + "seo_desc_packages": "Manage installed packages and system updates.", + "seo_desc_packages_installed": "List, search, and remove installed software packages.", + "seo_desc_packages_updates": "Check and install available package updates.", + "seo_desc_root": "Web-based server management dashboard.", + "seo_desc_services": "List, filter, and manage systemd service units.", + "seo_desc_services_detail": "Manage and monitor a systemd service unit.", + "seo_desc_storage": "Filesystem mounts and fstab configuration.", + "seo_desc_storage_fstab": "Persistent mount definitions in /etc/fstab.", + "seo_desc_storage_mounts": "Active filesystem mounts on this machine.", + "seo_desc_system": "Date and time, hostname, localization and power controls.", + "seo_desc_system_datetime": "Clock, timezone and time synchronisation settings.", + "seo_desc_system_hostname": "View and change the machine hostname.", + "seo_desc_system_localization": "Language, locale and region settings.", + "seo_desc_system_nadir": "View active modules and permission matrix for the connected Nadir agent.", + "seo_desc_system_power": "Reboot or power off the machine.", + "seo_desc_users": "PAM/Unix user accounts on this machine.", + "seo_desc_users_detail": "View and manage a PAM/Unix user account.", + "seo_desc_users_groups": "Unix groups from /etc/group on this machine.", + "seo_desc_users_groups_detail": "View and manage a Unix group.", + "seo_title_admin_config": "Config", + "seo_title_admin_users": "Users", + "seo_title_auth_2fa": "Two-factor authentication", + "seo_title_auth_forgot_password": "Forgot password", + "seo_title_auth_reset_password": "Reset password", + "seo_title_auth_setup_2fa": "Set up two-factor authentication", + "seo_title_auth_sign_in": "Sign in", + "seo_title_auth_sign_up": "Sign up", + "seo_title_dashboard": "Dashboard", + "seo_title_networking": "Networking", + "seo_title_networking_dns": "DNS", + "seo_title_networking_hosts": "Hosts", + "seo_title_networking_interfaces": "Interfaces", + "seo_title_networking_routes": "Routes", + "seo_title_packages": "Packages", + "seo_title_packages_installed": "Installed packages", + "seo_title_packages_updates": "Available updates", + "seo_title_root": "Home", + "seo_title_services": "Services", + "seo_title_storage": "Storage", + "seo_title_storage_fstab": "Fstab", + "seo_title_storage_mounts": "Mounts", + "seo_title_system": "System", + "seo_title_system_datetime": "Date and time", + "seo_title_system_hostname": "Hostname", + "seo_title_system_localization": "Localization", + "seo_title_system_nadir": "Nadir Agent", + "seo_title_system_power": "Power", + "seo_title_users": "System users", + "seo_title_users_groups": "Groups", + "services_action_disable": "Disable", + "services_action_disabled": "Service {name} disabled successfully.", + "services_action_enable": "Enable", + "services_action_enabled": "Service {name} enabled successfully.", + "services_action_restart": "Restart", + "services_action_restarted": "Service {name} restarted successfully.", + "services_action_start": "Start", + "services_action_started": "Service {name} started successfully.", + "services_action_stop": "Stop", + "services_action_stopped": "Service {name} stopped successfully.", + "services_active_filter": "Active State", + "services_col_active": "Active State", + "services_col_description": "Description", + "services_col_load": "Load State", + "services_col_sub": "Sub State", + "services_col_unit": "Unit", + "services_description": "List, filter, and manage systemd service units.", + "services_details_active_state": "Active State", + "services_details_load_state": "Load State", + "services_details_sub_state": "Sub State", + "services_details_title": "Service Details", + "services_details_unit_file_state": "Startup Type", + "services_filter_active": "Active", + "services_filter_dead": "Dead", + "services_filter_error": "Error", + "services_filter_exited": "Exited", + "services_filter_failed": "Failed", + "services_filter_inactive": "Inactive", + "services_filter_loaded": "Loaded", + "services_filter_masked": "Masked", + "services_filter_not_found": "Not Found", + "services_filter_other": "Other", + "services_filter_running": "Running", + "services_filter_title": "Filter Services", + "services_load_filter": "Load State", + "services_logs_autoscroll": "Autoscroll", + "services_logs_clear": "Clear", + "services_logs_empty": "No log entries.", + "services_logs_filters": "Filters", + "services_logs_lines": "Lines", + "services_logs_live": "Live", + "services_logs_live_streaming": "Streaming", + "services_logs_priority": "Max priority", + "services_logs_search_placeholder": "Filter log lines...", + "services_logs_since": "Since", + "services_logs_since_15m": "Last 15 minutes", + "services_logs_since_1h": "Last hour", + "services_logs_since_6h": "Last 6 hours", + "services_logs_since_all": "All", + "services_logs_since_today": "Today", + "services_logs_since_yesterday": "Since yesterday", + "services_logs_title": "Logs", + "services_no_services": "No services found.", + "services_search_placeholder": "Search service units...", + "services_sub_filter": "Sub State", + "services_title": "Services", + "settings": "Settings", + "setup_2fa_description": "Add an extra layer of security to your account.", + "setup_2fa_title": "Set up two-factor authentication", + "sign_up": "Sign up", + "sign_up_description": "Sign up with your email and a username.", + "sign_up_title": "Create your account", + "storage_badge_fstab": "fstab", + "storage_col_device": "Device", + "storage_col_dump": "Dump", + "storage_col_fstype": "Type", + "storage_col_mountpoint": "Mount point", + "storage_col_options": "Options", + "storage_col_pass": "Pass", + "storage_filter_show_pseudo": "Show pseudo filesystems", + "storage_filter_show_pseudo_hint": "proc, sysfs, tmpfs, cgroup, …", + "storage_fstab_description": "Persistent mount definitions from /etc/fstab.", + "storage_mount_add": "Add mount", + "storage_mount_add_description": "Appends an /etc/fstab entry and mounts it. If the mount fails the entry is rolled back.", + "storage_mount_added": "Filesystem mounted", + "storage_mount_remove": "Unmount", + "storage_mount_remove_description": "Unmounts the filesystem and removes its /etc/fstab entry. This cannot be undone.", + "storage_mount_remove_title": "Unmount {mountpoint}?", + "storage_mount_removed": "Filesystem unmounted", + "storage_mounts_description": "Active mounts from the kernel mount table.", + "storage_mounts_search_placeholder": "Search by device, mount point or type…", + "storage_no_fstab": "No fstab entries.", + "storage_no_mounts": "No mounts found.", + "system_hostname_current": "Current hostname", + "system_hostname_invalid": "Hostname is invalid", + "system_locale_generate": "Generate new locale", + "system_locale_generate_button": "Generate", + "system_locale_generate_desc": "Install a new locale on the host. On Debian/Ubuntu/Arch this uncomments the entry in /etc/locale.gen and runs locale-gen; on RHEL/Fedora it uses localedef.", + "system_locale_generate_invalid": "Use the form xx_XX.UTF-8 (e.g. fr_FR.UTF-8)", + "system_locale_generate_placeholder": "e.g. ja_JP.UTF-8", + "system_locale_keymap": "Console keymap", + "system_locale_lang": "System locale (LANG)", + "system_locale_language": "Fallback language (LANGUAGE)", + "system_locale_language_add": "Add language", + "system_locale_language_button": "Save", + "system_locale_language_desc": "Set the fallback language priority list for system messages and translations (optional).", + "system_locale_language_empty": "No fallback language set.", + "system_locale_no_keymap_found": "No keymap found.", + "system_locale_no_locale_found": "No locale found.", + "system_locale_search_keymap_placeholder": "Search keymap…", + "system_locale_search_locale_placeholder": "Search locale…", + "system_locale_x11": "X11 layout", + "system_power_confirm_description": "The machine will be unreachable while it shuts down. This cannot be undone from here.", + "system_power_confirm_poweroff_title": "Power off this machine?", + "system_power_confirm_reboot_title": "Reboot this machine?", + "system_power_poweroff": "Power off", + "system_power_reboot": "Reboot", + "system_section_configuration": "Configuration", + "system_section_identity": "Identity", + "system_section_power": "Power", + "system_time_clock": "Clock", + "system_time_current": "Current time", + "system_time_manual": "Manual time", + "system_time_manual_hint": "Set the system clock to a specific RFC3339 time. Available only when NTP is off.", + "system_time_no_timezone_found": "No timezone found.", + "system_time_ntp": "Network time (NTP)", + "system_time_ntp_hint": "Automatically synchronize the clock with NTP servers.", + "system_time_ntp_not_synced": "Not synchronized", + "system_time_ntp_synced": "Synchronized", + "system_time_search_timezone_placeholder": "Search timezone…", + "system_time_timezone": "Timezone", + "syslog_alert": "1 alert", + "syslog_crit": "2 crit", + "syslog_debug": "7 debug", + "syslog_emerg": "0 emerg", + "syslog_err": "3 err", + "syslog_info": "6 info", + "syslog_notice": "5 notice", + "syslog_warning": "4 warning", + "terms_notice": "By clicking continue, you agree to our Terms of Service and Privacy Policy.", "terminal_auth_private_key": "Private Key", "terminal_credential_cleared": "Saved credentials cleared", "terminal_credential_forgotten": "Forgot saved credentials for {username}", "terminal_credential_load_failed": "Could not load saved credentials", "terminal_credential_saved_toast": "Credentials saved for {username}", - "terminal_connect": "Connect", + "terminal_connect": "Connect", "terminal_connect_desc": "Connect to this host via SSH.", "terminal_discard": "Discard", "terminal_download_history": "Download history", "terminal_forget": "Forget", "terminal_history_truncated": "Output truncated, last 2 MB shown", - "terminal_connecting": "Connecting…", - "terminal_connecting_desc": "Opening SSH session to {credential} on port {port}", + "terminal_connecting": "Connecting…", + "terminal_connecting_desc": "Opening SSH session to {credential} on port {port}", "terminal_loaded_from_saved": "Loaded saved credentials", "terminal_password_placeholder": "Enter password", - "terminal_port": "Port", + "terminal_port": "Port", "terminal_private_key_placeholder": "Paste your SSH private key (including headers)", "terminal_remember_credential": "Remember credentials", "terminal_saved_credential_for": "Saved credentials for {username}", "terminal_section_auth": "Authentication", - "terminal_section_connection": "Connection", + "terminal_section_connection": "Connection", "terminal_session_ended": "Session ended", "terminal_ssh_failed": "SSH connection failed", "terminal_use_saved": "Use saved", - "theme": "Theme", - "theme_dark": "Dark", - "theme_light": "Light", - "theme_system": "System", - "trust_device": "Trust this device for 30 days", - "two_factor_description": "Enter the 6-digit code from your authenticator app.", - "two_factor_title": "Two-factor authentication", - "use_authenticator": "Use authenticator app", - "use_backup_code": "Use a backup code", - "username": "Username", - "username_placeholder": "admin", - "users_action_set_password": "Set password", - "users_actions": "Actions", - "users_active": "Active", - "users_add": "Add User", - "users_ban": "Ban", - "users_ban_reason": "Reason (optional)", - "users_banned": "Banned", - "users_col_comment": "Comment", - "users_col_home": "Home", - "users_col_type": "Type", - "users_col_uid": "UID", - "users_create": "Create", - "users_create_description": "Add a new user to the system.", - "users_create_field_comment": "Comment (GECOS)", - "users_create_field_create_home": "Create home directory", - "users_create_field_shell": "Shell", - "users_create_field_system": "System account", - "users_create_title": "Create user", - "users_created": "User created", - "users_created_at": "Joined", - "users_delete": "Delete", - "users_delete_ban_email": "Also ban this email", - "users_delete_confirm_description": "This permanently removes the user. Optionally ban the email to prevent re-registration.", - "users_delete_confirm_title": "Delete user?", - "users_delete_description": "Runs userdel on the host. This cannot be undone.", - "users_delete_field_remove_home": "Also remove home directory and mail spool", - "users_delete_title": "Delete {username}?", - "users_deleted": "User deleted", - "users_description": "Manage application users.", - "users_details": "Details", - "users_edit": "Edit", - "users_edit_description": "Update user details.", - "users_edit_title": "Edit user", - "users_filter": "Filter", - "users_filter_24h": "Last 24h", - "users_filter_30d": "Last 30 days", - "users_filter_7d": "Last 7 days", - "users_filter_active": "Active", - "users_filter_any_time": "Any Time", - "users_filter_count": "{n} filters active", - "users_filter_date_from": "From", - "users_filter_date_range": "Date Range", - "users_filter_date_to": "To", - "users_filter_display": "Display", - "users_filter_email_verified": "Email verified only", - "users_filter_joined": "Joined", - "users_filter_online_only": "Online users only", - "users_filter_reset": "Reset All", - "users_filter_shell_only": "Login-capable shell only", - "users_filter_show_banned": "Show banned users", - "users_filter_show_system": "Show system users", - "users_filter_system_uid_hint": "(uid < 1000)", - "users_filter_title": "Filter Users", - "users_group_primary_badge": "(primary)", - "users_group_sys_badge": "sys", - "users_groups_title": "Groups", - "users_groups_updated": "Groups updated", - "users_invite": "Invite", - "users_invite_description": "Send an email invitation. The user sets their own password.", - "users_invite_title": "Invite user", - "users_invited": "Invitation sent", - "users_nav_description": "PAM/Unix accounts from /etc/passwd on this machine.", - "users_nav_title": "System users", - "users_next": "Next", - "users_no_gecos": "No GECOS comment", - "users_no_groups": "No groups.", - "users_no_results": "No users found.", - "users_page_of": "Page {page} of {total}", - "users_pam_create_description": "Adds a PAM account via useradd. Password stays locked until you set one.", - "users_pam_groups_description": "Supplementary groups. Replaces the full set via usermod -G. Primary group is set at user creation and not editable here.", - "users_pam_search_placeholder": "Search username, GECOS, uid…", - "users_pending": "Pending", - "users_pending_expires": "Invite expires {date}", - "users_pending_no_invite": "Email not verified", - "users_prev": "Previous", - "users_primary_gid": "Primary GID", - "users_primary_group": "Primary group", - "users_resend_invite": "Resend invite", - "users_role": "Role", - "users_role_admin": "Admin", - "users_role_user": "User", - "users_rows_per_page": "Rows per page", - "users_saved": "User saved", - "users_search_placeholder": "Search by email…", - "users_set_password_description": "Piped to chpasswd over stdin; never appears in the process list.", - "users_set_password_title": "Set password — {username}", - "users_status": "Status", - "users_title": "Users", - "users_type_system": "system", - "users_type_user": "user", - "users_unban": "Unban", - "verification_sent": "We sent a verification link to {email}. Click it to activate your account.", - "verify": "Verify", - "verify_your_email": "You need to first verify your email address", - "welcome_back": "Welcome back", - "system_nadir_username": "Authenticated User", - "system_nadir_permissions": "Resolved Permissions", - "system_nadir_modules": "Registered Modules", - "system_nadir_no_permissions": "No permissions defined" + "theme": "Theme", + "theme_dark": "Dark", + "theme_light": "Light", + "theme_system": "System", + "trust_device": "Trust this device for 30 days", + "two_factor_description": "Enter the 6-digit code from your authenticator app.", + "two_factor_title": "Two-factor authentication", + "use_authenticator": "Use authenticator app", + "use_backup_code": "Use a backup code", + "username": "Username", + "username_placeholder": "admin", + "users_action_set_password": "Set password", + "users_actions": "Actions", + "users_active": "Active", + "users_add": "Add User", + "users_ban": "Ban", + "users_ban_reason": "Reason (optional)", + "users_banned": "Banned", + "users_col_comment": "Comment", + "users_col_home": "Home", + "users_col_type": "Type", + "users_col_uid": "UID", + "users_create": "Create", + "users_create_description": "Add a new user to the system.", + "users_create_field_comment": "Comment (GECOS)", + "users_create_field_create_home": "Create home directory", + "users_create_field_shell": "Shell", + "users_create_field_system": "System account", + "users_create_title": "Create user", + "users_created": "User created", + "users_created_at": "Joined", + "users_delete": "Delete", + "users_delete_ban_email": "Also ban this email", + "users_delete_confirm_description": "This permanently removes the user. Optionally ban the email to prevent re-registration.", + "users_delete_confirm_title": "Delete user?", + "users_delete_description": "Runs userdel on the host. This cannot be undone.", + "users_delete_field_remove_home": "Also remove home directory and mail spool", + "users_delete_title": "Delete {username}?", + "users_deleted": "User deleted", + "users_description": "Manage application users.", + "users_details": "Details", + "users_edit": "Edit", + "users_edit_description": "Update user details.", + "users_edit_title": "Edit user", + "users_filter": "Filter", + "users_filter_24h": "Last 24h", + "users_filter_30d": "Last 30 days", + "users_filter_7d": "Last 7 days", + "users_filter_active": "Active", + "users_filter_any_time": "Any Time", + "users_filter_count": "{n} filters active", + "users_filter_date_from": "From", + "users_filter_date_range": "Date Range", + "users_filter_date_to": "To", + "users_filter_display": "Display", + "users_filter_email_verified": "Email verified only", + "users_filter_joined": "Joined", + "users_filter_online_only": "Online users only", + "users_filter_reset": "Reset All", + "users_filter_shell_only": "Login-capable shell only", + "users_filter_show_banned": "Show banned users", + "users_filter_show_system": "Show system users", + "users_filter_system_uid_hint": "(uid < 1000)", + "users_filter_title": "Filter Users", + "users_group_primary_badge": "(primary)", + "users_group_sys_badge": "sys", + "users_groups_title": "Groups", + "users_groups_updated": "Groups updated", + "users_invite": "Invite", + "users_invite_description": "Send an email invitation. The user sets their own password.", + "users_invite_title": "Invite user", + "users_invited": "Invitation sent", + "users_nav_description": "PAM/Unix accounts from /etc/passwd on this machine.", + "users_nav_title": "System users", + "users_next": "Next", + "users_no_gecos": "No GECOS comment", + "users_no_groups": "No groups.", + "users_no_results": "No users found.", + "users_page_of": "Page {page} of {total}", + "users_pam_create_description": "Adds a PAM account via useradd. Password stays locked until you set one.", + "users_pam_groups_description": "Supplementary groups. Replaces the full set via usermod -G. Primary group is set at user creation and not editable here.", + "users_pam_search_placeholder": "Search username, GECOS, uid…", + "users_pending": "Pending", + "users_pending_expires": "Invite expires {date}", + "users_pending_no_invite": "Email not verified", + "users_prev": "Previous", + "users_primary_gid": "Primary GID", + "users_primary_group": "Primary group", + "users_resend_invite": "Resend invite", + "users_role": "Role", + "users_role_admin": "Admin", + "users_role_user": "User", + "users_rows_per_page": "Rows per page", + "users_saved": "User saved", + "users_search_placeholder": "Search by email…", + "users_set_password_description": "Piped to chpasswd over stdin; never appears in the process list.", + "users_set_password_title": "Set password — {username}", + "users_status": "Status", + "users_title": "Users", + "users_type_system": "system", + "users_type_user": "user", + "users_unban": "Unban", + "verification_sent": "We sent a verification link to {email}. Click it to activate your account.", + "verify": "Verify", + "verify_your_email": "You need to first verify your email address", + "welcome_back": "Welcome back", + "system_nadir_username": "Authenticated User", + "system_nadir_permissions": "Resolved Permissions", + "system_nadir_modules": "Registered Modules", + "system_nadir_no_permissions": "No permissions defined", + "docs_title": "Docs", + "docs_go_to_dashboard": "Go to Dashboard", + "docs_nav_intro": "Introduction", + "docs_nav_installation": "Installation", + "docs_nav_architecture": "Architecture", + "docs_nav_security": "Security & VPN", + "docs_nav_limitations": "Limitations & License", + "docs_intro_title": "Introduction", + "docs_intro_desc": "Welcome to the Nadir documentation. Nadir is a lightweight, modular Linux system-administration suite that provides modern, self-hosted web management panels for your servers.", + "docs_intro_how_title": "How it works", + "docs_intro_how_desc": "Nadir is divided into two distinct parts: a central Web UI (Frontend) and a per-host Agent (Backend). The frontend does not talk to your operating system directly; instead, it registers multiple agent nodes using secure API tokens and makes server-to-server HTTP queries to manage your systems.", + "docs_intro_modules_title": "Core modules", + "docs_intro_modules_desc": "Functionality is organized into isolated modules. Each module manages a slice of system configuration and defines its own role-based access control (RBAC) permission vocabulary.", + "docs_intro_mod_system_desc": "Dashboard overview (OS, kernel, CPU, memory, load, uptime, temperatures), hostname configuration, and date/time/NTP settings.", + "docs_intro_mod_services_desc": "List and inspect systemd units. Start, stop, restart, enable, or disable units, and stream journald logs live over SSE.", + "docs_intro_mod_users_desc": "Manage local PAM/Unix user accounts and group assignments. Run useradd/groupadd utilities natively.", + "docs_intro_mod_storage_desc": "Monitor storage devices, partition layouts, and active mounts. View and modify /etc/fstab configurations.", + "docs_intro_mod_networking_desc": "Manage network interfaces, routing tables, and DNS settings. Supports temporary configuration and safety auto-rollback.", + "docs_intro_mod_audit_desc": "Read-only, tamper-evident SQLite-backed audit log capturing who executed what, when, and the result.", + "docs_intro_mod_audit_title": "Audit Trail", + "docs_intro_ready_title": "Ready to deploy?", + "docs_intro_ready_desc": "Proceed to the installation guide to spin up the Web UI and deploy the backend agent.", + "docs_intro_ready_cta": "Get started", + "docs_install_title": "Installation", + "docs_install_desc": "Deploy Nadir in under two minutes. Run the central Web UI as a stateless Docker container and bootstrap agent nodes on target Linux hosts.", + "docs_install_webui_title": "1. Deploy the Web UI", + "docs_install_webui_desc": "The central dashboard operates as a stateless web server that registers and coordinates multiple node instances. Use Docker to spin it up in seconds:", + "docs_install_webui_run": "Once the container is running, navigate to http://localhost:3000 to register your administrator account.", + "docs_install_agent_title": "2. Bootstrap agent nodes", + "docs_install_agent_desc": "Install the nadir-agent backend daemon on any Linux machine you want to manage. Run the bootstrap script on the host:", + "docs_install_disclaimer_title": "Disclaimer: Inspect Before Execution", + "docs_install_disclaimer_desc": "Piping untrusted scripts directly from the internet into a root shell is extremely dangerous. You should always inspect and read the source code of any script before running it. Review the installer code served by this instance at /install.sh or download it to read first: curl -fsSL https://{nadirHost}/install.sh.", + "docs_install_privilege_title": "Security & Privileges", + "docs_install_privilege_desc": "The bootstrap script must run as root (via sudo sh). The agent requires root privileges because it executes PAM authorization, checks systemd state, mounts filesystems, and interacts with /etc/shadow and /etc/hosts directly.", + "docs_install_tasks_intro": "The installer script performs the following tasks automatically:", + "docs_install_task_1": "Detects the system processor architecture (amd64 or arm64).", + "docs_install_task_2": "Queries the configured Gitea repository API for the latest tagged binary release.", + "docs_install_task_3": "Downloads the release asset and verifies its SHA-256 signature against the published checksum file.", + "docs_install_task_4": "Installs the nadir executable to /usr/local/bin/nadir.", + "docs_install_task_5": "Provisions the dedicated PAM service /etc/pam.d/nadir to enable password validation.", + "docs_install_task_6": "Generates persistent self-signed TLS certificates under /var/lib/nadir/tls (if TLS is enabled).", + "docs_install_task_7": "Creates and starts a systemd service unit (nadir.service) that starts on boot.", + "docs_install_link_title": "3. Link nodes to dashboard", + "docs_install_link_desc": "When the installer finishes, it generates a bearer token (starting with nad_) for dashboard authentication. Copy this token, log into the Web UI, click Add Server, enter the IP address and port, paste the token, and save.", + "docs_install_link_manual": "To manually add additional authentication tokens on a host, run:", + "docs_arch_title": "Architecture", + "docs_arch_desc": "Nadir is engineered for high performance, isolation, and security. Learn about the technical implementation details that drive the Web UI and the Agent.", + "docs_arch_bounds_title": "Technical boundaries", + "docs_arch_bounds_desc": "The central Web UI and the individual agent instances interact over standard HTTPS connections using token-based authentication.", + "docs_arch_card_webui_title": "Nadir Web UI (Frontend)", + "docs_arch_card_webui_desc": "SvelteKit 2 + Svelte 5 console application", + "docs_arch_card_webui_item1": "SQLite Database for machine lists and profiles", + "docs_arch_card_webui_item2": "Better Auth session and security layers", + "docs_arch_card_webui_item3": "Stateless deployment in Docker", + "docs_arch_card_flow_title": "HTTPS / API Call", + "docs_arch_card_flow_desc": "Bearer token auth", + "docs_arch_card_agent_title": "Nadir Agent (Backend)", + "docs_arch_card_agent_desc": "Lightweight daemon compiled in Go", + "docs_arch_card_agent_item1": "Runs as root with PAM authentication + RBAC", + "docs_arch_card_agent_item2": "Direct communication with systemd & packages", + "docs_arch_card_agent_item3": "SQLite-backed local audit trail", + "docs_arch_daemon_title": "Self-contained Go agent", + "docs_arch_daemon_desc": "The agent daemon runs on each host machine as a systemd service. It is designed to be fully self-contained and does not require external databases, runtimes, or interpreters.", + "docs_arch_daemon_desc2": "Instead of implementing custom system logic, the Go daemon acts as an API proxy over the host's native utilities:", + "docs_arch_daemon_item1": "Systemd controls service operations via direct interactions with systemd units.", + "docs_arch_daemon_item2": "PAM (Pluggable Authentication Modules) handles credentials and account validations.", + "docs_arch_daemon_item3": "OS Utilities like useradd, userdel, groupadd, groupdel, hostnamectl, and filesystem tools perform account, host, and storage changes.", + "docs_arch_daemon_item4": "Package Managers (APT on Debian/Ubuntu, DNF on RHEL/Fedora, Pacman on Arch) perform software package updates and upgrades, streaming outputs live using Server-Sent Events (SSE).", + "docs_arch_pam_title": "Dedicated PAM integration", + "docs_arch_pam_desc": "To authenticate human logins, the agent delegates password checks to PAM (pam_unix). Instead of using default system login services, the agent installs a dedicated, minimal PAM service file at /etc/pam.d/nadir:", + "docs_arch_pam_desc2": "This specific setup prevents performance issues common with stock login services. For instance, on laptops, the default system-auth pulls in pam_fprintd.so which blocks password verification for up to 30 seconds while waiting for fingerprint swipes. A dedicated PAM service keeps login validation times down to milliseconds.", + "docs_arch_minisign_title": "Secure auto-updates via Minisign", + "docs_arch_minisign_desc": "To ensure agent updates are secure, the updater implements strict cryptographic signature verification:", + "docs_arch_minisign_item1": "The agent queries the configured Gitea release API to fetch the latest tagged release.", + "docs_arch_minisign_item2": "It downloads both the checksum list file (sha256sums.txt) and its signature file (sha256sums.txt.minisig).", + "docs_arch_minisign_item3": "It verifies the signature file against the hardcoded Minisign public key (minisign.pub) compiled into the binary. This trust anchor cannot be changed without rebuilding the binary.", + "docs_arch_minisign_item4": "If the signature checks out, the agent downloads the new binary, verifies its SHA-256 hash against the verified checksum file, atomically replaces the running binary at /usr/local/bin/nadir, and restarts the systemd unit.", + "docs_security_title": "Security & VPN", + "docs_security_desc": "Because Nadir executes system commands as root, maintaining strict network boundaries is the most critical element of a secure deployment.", + "docs_security_warning_title": "Warning: Root Access Risks", + "docs_security_warning_desc": "An instance of nadir-agent running on a host holds root privileges. Exposing the agent port directly to the public internet is extremely dangerous and strongly discouraged. By default, the agent binds to localhost to ensure it remains local-only.", + "docs_security_deploy_title": "Suggested deployments", + "docs_security_deploy_desc": "Choose one of these recommended deployment strategies to isolate your system administration ports:", + "docs_security_deploy_coloc_title": "A. Co-Location (Loopback)", + "docs_security_deploy_coloc_desc": "Install both the Nadir Web UI and the nadir-agent on the exact same virtual machine or server. Configure the agent to bind to 127.0.0.1:9999 and let the Web UI container access the local agent directly. This keeps the agent API completely unexposed to any external network interface.", + "docs_security_deploy_vpn_title": "B. Private Overlay VPN (Recommended)", + "docs_security_deploy_vpn_desc": "For managing multiple servers, configure an overlay VPN (such as Tailscale, Netbird, or WireGuard). Bind the nadir-agent instance only to the host's private overlay IP (e.g. 100.64.x.x). This guarantees that only authenticated VPN peers can see the administration port.", + "docs_security_acl_title": "Access control list (ACL) rules", + "docs_security_acl_desc": "If you deploy over a private overlay network, restrict access so that only the central Web UI server is permitted to talk to the agent ports (tcp/9999):", + "docs_security_acl_tailscale": "Tailscale: Add an ACL rule restricting target ports to the proxy tag:", + "docs_security_acl_netbird": "Netbird: Setup an Access Control Policy permitting group nadir-ui to group nadir-agent on port 9999, denying other peers.", + "docs_security_acl_iptables": "Standard iptables: Reject all connections on the WireGuard interface except from the proxy peer:", + "docs_security_tls_title": "TLS connection modes", + "docs_security_tls_desc": "API keys, tokens, and session cookies must always travel over encrypted HTTPS connections. Secure the connection using one of three modes:", + "docs_security_tls_proxy_desc": "Behind a reverse proxy (trust_proxy: true): A proxy like Traefik terminates TLS at the edge and forwards plaintext HTTP to the agent over a secure local network. In this setup, make sure the proxy overrides client-supplied X-Forwarded-* headers, and set secure_tls: true to ensure the session cookie keeps its Secure attribute.", + "docs_security_tls_direct_desc": "Direct TLS termination (tls_cert + tls_key): Specify paths to a valid PEM certificate and key file in config.yaml to have the agent server terminate TLS directly.", + "docs_security_tls_self_desc": "Self-signed certificate fallback (development only): If no proxy or certificates are configured, the agent generates an in-memory self-signed certificate valid for localhost for one year. This triggers browser security warnings and should never be used in production.", + "docs_limits_title": "Limitations & License", + "docs_limits_desc": "Review the current architectural limits of the agent and the open-source licensing terms of the Nadir project.", + "docs_limits_sys_title": "System limitations", + "docs_limits_sys_desc": "Because Nadir works directly with core operating system abstractions rather than virtualization layers, it is bound by the following environmental requirements:", + "docs_limits_sys_item1": "Linux Only: The agent relies heavily on standard Linux utilities (useradd, groupadd, shutdown), kernel namespaces, and file systems under /sys, /proc, and /etc. It cannot run on macOS, Windows, or BSD distributions.", + "docs_limits_sys_item2": "Systemd Dependency: The service management, boot status, and journal logging modules interact directly with systemd and journald. Runtimes using other init systems (like sysvinit, OpenRC, or Runit) are not supported.", + "docs_limits_sys_item3": "Root Privileges Required: The agent must execute as root. The PAM authenticator needs read access to /etc/shadow, and managing hardware settings (hostname, network interfaces, disk mounts) is locked behind root level syscalls by the kernel.", + "docs_limits_sys_item4": "Co-hosting CORS constraints: Running the Web UI cross-origin relative to target agents triggers browser CORS preflight checks. If you host the frontend on a different origin, you must utilize server-to-server dashboard API queries (default behavior of SvelteKit server load functions) or configure a reverse proxy to terminate both under a shared domain.", + "docs_limits_license_title": "Licensing", + "docs_limits_license_desc": "Nadir is free, open-source software published under the terms of the MIT License.", + "docs_limits_license_mit_title": "MIT License", + "docs_limits_license_mit_copyright": "Copyright (c) 2026 Urania", + "docs_limits_license_mit_body1": "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "docs_limits_license_mit_body2": "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "docs_limits_license_mit_body3": "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "docs_limits_credits_title": "Credits & assets", + "docs_limits_credits_desc": "Nadir's branding assets and icons are based on open-source packages:", + "docs_limits_credits_item1": "Favicon: Recolored version of the Orbit icon from the Lucide project.", + "docs_limits_credits_item2": "Icons: Visual markers across pages are provided by the Lucide icons library, licensed under the ISC License." } diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 880c295..4941b96 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -17,7 +17,8 @@ const handleBetterAuth: Handle = async ({ event, resolve }) => { event.locals.user = session.user; } else if ( !event.url.pathname.startsWith('/auth') && - !event.url.pathname.startsWith('/api/auth') + !event.url.pathname.startsWith('/api/auth') && + event.url.pathname !== '/install.sh' ) { redirect(307, '/auth/sign-in'); } @@ -39,6 +40,7 @@ const handleBetterAuth: Handle = async ({ event, resolve }) => { .filter(Boolean); if (!roles.includes('admin')) redirect(307, '/dashboard'); } + return svelteKitHandler({ auth, building, event, resolve }); }; diff --git a/src/lib/components/blocks/sidebar/machines-nav.svelte b/src/lib/components/blocks/sidebar/machines-nav.svelte index 7329527..ecdb27a 100644 --- a/src/lib/components/blocks/sidebar/machines-nav.svelte +++ b/src/lib/components/blocks/sidebar/machines-nav.svelte @@ -40,7 +40,7 @@ let listEl: HTMLDivElement | undefined = $state(); let items: { address: string; id: string; name: null | string }[] = $state([]); let reordering = $state(false); - let inst = $state(null); + let inst = $state(null); onDestroy(() => inst?.destroy()); @@ -86,7 +86,7 @@ untrack(() => { const cur = [...items.map((i) => i.id)].sort().join('|'); const next = [...data.items.map((i) => i.id)].sort().join('|'); - if (cur !== next) { + if (cur !== next) { items = data.items; reordering = false; inst?.destroy(); @@ -115,9 +115,7 @@ open = false; } catch (error) { console.error(error); - toast.error( - extractErrorMessage(error) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(error) ?? m.errors_generic()); } })} > diff --git a/src/lib/components/blocks/terminal-dialog.svelte b/src/lib/components/blocks/terminal-dialog.svelte index 5488429..61f5c94 100644 --- a/src/lib/components/blocks/terminal-dialog.svelte +++ b/src/lib/components/blocks/terminal-dialog.svelte @@ -36,10 +36,7 @@ function stripAnsi(text: string): string { const E = String.fromCharCode(27); const B = String.fromCharCode(7); - const control = new RegExp( - `[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]`, - 'g' - ); + const control = new RegExp(`[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]`, 'g'); return text .replace(new RegExp(`${E}\\[[0-9;<=>?]*[a-zA-Z]`, 'g'), '') .replace(new RegExp(`${E}\\][0-9;]*[^${E}]*(?:${E}\\\\|${B})`, 'g'), '') @@ -121,9 +118,7 @@ view = 'terminal'; } catch (err) { view = 'form'; - toast.error( - extractErrorMessage(err) ?? m.terminal_ssh_failed() - ); + toast.error(extractErrorMessage(err) ?? m.terminal_ssh_failed()); localStorage.removeItem(storageKey); } finally { loadingSaved = false; @@ -171,9 +166,7 @@ view = 'terminal'; } catch (err) { view = 'form'; - toast.error( - extractErrorMessage(err) ?? m.terminal_ssh_failed() - ); + toast.error(extractErrorMessage(err) ?? m.terminal_ssh_failed()); } } @@ -279,9 +272,7 @@ } catch (err) { if (active) { sessionEnded = true; - toast.error( - extractErrorMessage(err) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(err) ?? m.errors_generic()); } } })(); @@ -311,11 +302,14 @@ }); - { if (!o && sessionActive) return; if (!o) handleClose(); }}> - { + if (!o && sessionActive) return; + if (!o) handleClose(); + }} > + {#if view === 'form'}
@@ -333,12 +327,16 @@
-

{m.terminal_section_connection()}

+

+ {m.terminal_section_connection()} +

- +
-

{m.terminal_section_auth()}

-
+

+ {m.terminal_section_auth()} +

+
{#if hasSaved && !loadingSaved}
@@ -389,13 +389,17 @@ > {m.password()} - {m.terminal_auth_private_key()} + {m.terminal_auth_private_key()}
- +
- + @@ -438,7 +439,6 @@ {m.terminal_connect()} - {:else if view === 'connecting'}
@@ -449,47 +449,57 @@

- {:else} -
-
-
-
- -
-
- {m.nav_system_terminal()} - - - {username}@{port} - -
-
-
-
-
- {#if sessionEnded} -
- - {capturedHistory.length > 2_000_000 ? m.terminal_history_truncated() : m.terminal_session_ended()} - -
- - +
+
+
+
+ +
+
+ {m.nav_system_terminal()} + + + {username}@{port} +
- {/if} +
+
+
+ {#if sessionEnded} +
+ + {capturedHistory.length > 2_000_000 + ? m.terminal_history_truncated() + : m.terminal_session_ended()} + +
+ + +
+
+ {/if} +
-
{/if} diff --git a/src/lib/components/dashboard/activity-panel.svelte b/src/lib/components/dashboard/activity-panel.svelte index c92862b..0cca62c 100644 --- a/src/lib/components/dashboard/activity-panel.svelte +++ b/src/lib/components/dashboard/activity-panel.svelte @@ -14,7 +14,7 @@ title={m.dashboard_recent_activity()} icon={History} {items} - pageSize={10} + pageSize={25} initialDir="desc" columns={[ { get: (e) => e.time, key: 'time', label: m.dashboard_col_time() }, diff --git a/src/lib/components/dashboard/cpu-heatmap.svelte b/src/lib/components/dashboard/cpu-heatmap.svelte index b2e9692..1f53ef0 100644 --- a/src/lib/components/dashboard/cpu-heatmap.svelte +++ b/src/lib/components/dashboard/cpu-heatmap.svelte @@ -84,12 +84,12 @@
-
+
{#each cores as c (c.core)}
(pageSizeKey, defaultPageSize); - + const totalPages = $derived(Math.max(1, Math.ceil(items.length / pageSize.current))); - const paginated = $derived( - items.slice((page - 1) * pageSize.current, page * pageSize.current) - ); + const paginated = $derived(items.slice((page - 1) * pageSize.current, page * pageSize.current)); $effect(() => { if (page > totalPages) page = totalPages; diff --git a/src/lib/components/dashboard/format.ts b/src/lib/components/dashboard/format.ts index 5bc638b..4ba886c 100644 --- a/src/lib/components/dashboard/format.ts +++ b/src/lib/components/dashboard/format.ts @@ -15,6 +15,20 @@ export function uptime(seconds: number) { // first IPv4 (no colon), address only export const ipv4 = (addrs: null | string[]) => addrs?.find((a) => !a.includes(':'))?.split('/')[0]; +// Strip lspci noise from GPU model strings +export function trimGpuName(name: string): string { + return name + .replace(/^VGA compatible controller \[\w+\]:\s*/, '') + .replace( + /^(?:Advanced Micro Devices, Inc\. \[AMD\/ATI\]|NVIDIA Corporation|Intel Corporation)\s*/, + '' + ) + .replace(/\s*\[\w+:\w+\]\s*/g, ' ') + .replace(/\s*\(rev\s+\w+\)\s*/g, '') + .replace(/\s{2,}/g, ' ') + .trim(); +} + // TZ pinned to UTC so SSR (Node host TZ) and first client paint produce identical // strings — the locale comes from paraglide, so it matches whatever the user picked. // ponytail: swap timeZone to the browser's TZ post-mount if you want local time. diff --git a/src/lib/components/dashboard/gpu-card.svelte b/src/lib/components/dashboard/gpu-card.svelte new file mode 100644 index 0000000..a8d37f4 --- /dev/null +++ b/src/lib/components/dashboard/gpu-card.svelte @@ -0,0 +1,74 @@ + + + + +
+ + + + {m.dashboard_gpu()} +
+
+ + {#each gpus as gpu, i (gpu.vendor + gpu.device_id)} + {@const memPct = pct(gpu.memory_used_bytes, gpu.memory_total_bytes)} +
+
+ {trimGpuName(gpu.model)} + {gpu.driver} +
+
+
+ {m.dashboard_gpu_compute()} + {gpu.utilization_pct}% +
+ +
+ {#if gpu.memory_total_bytes > 0} +
+
+ {m.dashboard_gpu_vram()} + {gb(gpu.memory_used_bytes)} / {gb(gpu.memory_total_bytes)} +
+ +
+ {/if} +
+ {/each} +
+
diff --git a/src/lib/components/dashboard/storage-panel.svelte b/src/lib/components/dashboard/storage-panel.svelte index 0c557eb..191eab3 100644 --- a/src/lib/components/dashboard/storage-panel.svelte +++ b/src/lib/components/dashboard/storage-panel.svelte @@ -16,6 +16,7 @@ title={m.dashboard_storage()} icon={HardDrive} {items} + pageSize={5} initialDir="desc" columns={[ { get: (d) => pct(d.used_bytes, d.total_bytes), key: 'usage', label: m.dashboard_col_usage() }, diff --git a/src/lib/components/dashboard/system-panel.svelte b/src/lib/components/dashboard/system-panel.svelte index b42d380..421666a 100644 --- a/src/lib/components/dashboard/system-panel.svelte +++ b/src/lib/components/dashboard/system-panel.svelte @@ -27,7 +27,7 @@ opts: { class: string; mono: boolean } = { class: '', mono: false } )}
- {label} + {label} {value} ({ ...t, n: i + 1 }))); t.celsius, key: 'temp', label: m.dashboard_col_temp() }, - { get: (t) => t.label, key: 'sensor', label: m.dashboard_col_sensor() } + { get: (t) => t.chip, key: 'chip', label: m.dashboard_col_sensor() } ]} > {#snippet row(temp, i)} @@ -29,8 +27,11 @@ ? 'border-border/60 border-t' : ''}" > - {temp.label} #{temp.n} - + {temp.chip} + {temp.label} +
+ {temp.celsius.toFixed(1)} °C
diff --git a/src/lib/components/dashboard/types.ts b/src/lib/components/dashboard/types.ts index f0b1c1f..8901fbf 100644 --- a/src/lib/components/dashboard/types.ts +++ b/src/lib/components/dashboard/types.ts @@ -30,4 +30,4 @@ export type SystemDetails = { updates: { count: number; manager: string } | null; }; -export type Temp = { celsius: number; label: string }; +export type Temp = { celsius: number; chip: string; label: string }; diff --git a/src/lib/components/ui/field/field-label.svelte b/src/lib/components/ui/field/field-label.svelte index f5f8e8b..c7a4a20 100644 --- a/src/lib/components/ui/field/field-label.svelte +++ b/src/lib/components/ui/field/field-label.svelte @@ -16,7 +16,7 @@ bind:ref data-slot="field-label" class={cn( - 'has-data-checked:bg-primary/5 has-data-checked:border-primary/30 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 group/field-label peer/field-label flex w-fit leading-snug', + 'has-data-checked:bg-primary/5 has-data-checked:border-primary/30 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 group/field-label peer/field-label flex w-fit', 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col', className )} diff --git a/src/lib/const/schema.ts b/src/lib/const/schema.ts index 0cbb9b1..9726a17 100644 --- a/src/lib/const/schema.ts +++ b/src/lib/const/schema.ts @@ -31,6 +31,22 @@ const EnvSchema = v.object({ SMTP_USER: v.pipe(v.optional(v.string())) }); -const env = v.parse(EnvSchema, Bun.env); +const env = (() => { + const rawEnv = typeof Bun !== 'undefined' ? Bun.env : process.env; + // Mock defaults during build analysis when environment variables are not injected + const parsed = v.safeParse(EnvSchema, rawEnv); + if (parsed.issues) { + const fallbackEnv = { + CRYPTO_SECRET: + rawEnv.CRYPTO_SECRET || 'build_time_mock_crypto_secret_for_sveltekit_build_analysis_step', + REPOSITORY_URL: + rawEnv.REPOSITORY_URL || + 'https://tea.urania.dev/api/v1/repos/urania/nadir-agent/releases/latest', + ...rawEnv + }; + return v.parse(EnvSchema, fallbackEnv); + } + return parsed.output; +})(); export { env }; diff --git a/src/lib/remotes/machines.remote.ts b/src/lib/remotes/machines.remote.ts index e8685b6..7145b1a 100644 --- a/src/lib/remotes/machines.remote.ts +++ b/src/lib/remotes/machines.remote.ts @@ -71,6 +71,14 @@ export const machineHealth = query.batch(v.string(), async (machineIds) => { return (id: string) => online.get(id) ?? false; }); +export const allMachines = query(async () => { + const rows = await db + .select({ address: machines.address, id: machines.id, name: machines.name }) + .from(machines) + .orderBy(sql`${machines.order} IS NULL`, asc(machines.order), asc(machines.name)); + return rows; +}); + export const getMachine = query(v.string(), async (id) => { const row = await db .select({ address: machines.address, id: machines.id, name: machines.name }) diff --git a/src/lib/remotes/packages.remote.ts b/src/lib/remotes/packages.remote.ts index 6c96478..a0ccd97 100644 --- a/src/lib/remotes/packages.remote.ts +++ b/src/lib/remotes/packages.remote.ts @@ -77,7 +77,10 @@ export const streamPackageAction = query.live( } for await (const chunk of parseSseStream(response)) { - yield chunk as { data: { error: string; line: string; message: string; success: boolean }; event: string }; + yield chunk as { + data: { error: string; line: string; message: string; success: boolean }; + event: string; + }; } } ); diff --git a/src/lib/remotes/system.remote.ts b/src/lib/remotes/system.remote.ts index f5018c9..70c037d 100644 --- a/src/lib/remotes/system.remote.ts +++ b/src/lib/remotes/system.remote.ts @@ -65,7 +65,7 @@ export const setTime = command( export const systemHostname = query(v.string(), async (machineId) => { const { client: nadir } = await nadirForMachine(machineId); const { data, error: err } = await nadir.GET('/api/system/hostname'); - console.log(err) + console.log(err); if (!data) throwNadirError(err); return data; }); @@ -133,12 +133,15 @@ export const setLocale = command( } ); -export const generateLocale = command(v.object({ locale: v.string(), machineId: v.string() }), async ({ locale, machineId }) => { - const { client: nadir } = await nadirForMachine(machineId); - const { error: err } = await nadir.POST('/api/system/locale/generate', { body: { locale } }); - if (err) throwNadirError(err); - await listLocales(machineId).refresh(); -}); +export const generateLocale = command( + v.object({ locale: v.string(), machineId: v.string() }), + async ({ locale, machineId }) => { + const { client: nadir } = await nadirForMachine(machineId); + const { error: err } = await nadir.POST('/api/system/locale/generate', { body: { locale } }); + if (err) throwNadirError(err); + await listLocales(machineId).refresh(); + } +); export const getModules = query(v.string(), async (machineId) => { const { client: nadir } = await nadirForMachine(machineId); diff --git a/src/lib/remotes/terminal.remote.ts b/src/lib/remotes/terminal.remote.ts index 1c312c7..f1332aa 100644 --- a/src/lib/remotes/terminal.remote.ts +++ b/src/lib/remotes/terminal.remote.ts @@ -4,15 +4,15 @@ import { v } from '$lib'; import { env } from '$lib/const/schema'; import { m } from '$lib/paraglide/messages'; import { db } from '$lib/server/db'; -import { machines } from '$lib/server/db/schema'; import { decryptValue, encrypt } from '$lib/server/db/custom-types'; +import { machines } from '$lib/server/db/schema'; import { closeSession, createSession, getSession, resizeSession, - writeToSession, - type SessionAuth + type SessionAuth, + writeToSession } from '$lib/server/terminal/session'; import { eq } from 'drizzle-orm'; @@ -48,36 +48,33 @@ export const openSshTerminal = command( } ); -export const streamSshTerminal = query.live( - v.string(), - async function* (sessionId: string) { - const session = getSession(sessionId); - if (!session) { - yield { data: 'SSH connection failed or session expired', event: 'error' }; - return; - } - - if (session.outputStream.locked) { - throw error(409, 'Terminal stream is already being consumed'); - } - - const reader = session.outputStream.getReader(); - - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - yield { data: value, event: 'output' }; - } - } catch (err) { - const message = err instanceof Error ? err.message : 'Terminal connection lost'; - yield { data: message, event: 'error' }; - } finally { - reader.releaseLock(); - closeSession(sessionId); - } +export const streamSshTerminal = query.live(v.string(), async function* (sessionId: string) { + const session = getSession(sessionId); + if (!session) { + yield { data: 'SSH connection failed or session expired', event: 'error' }; + return; } -); + + if (session.outputStream.locked) { + throw error(409, 'Terminal stream is already being consumed'); + } + + const reader = session.outputStream.getReader(); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + yield { data: value, event: 'output' }; + } + } catch (err) { + const message = err instanceof Error ? err.message : 'Terminal connection lost'; + yield { data: message, event: 'error' }; + } finally { + reader.releaseLock(); + closeSession(sessionId); + } +}); export const writeSshTerminal = command( v.object({ data: v.string(), sessionId: v.string() }), @@ -100,16 +97,13 @@ export const closeSshTerminal = command(v.string(), async (sessionId: string) => return { ok: true }; }); -export const encryptCredential = command( - v.object({ data: v.string() }), - async ({ data }) => { - const { - locals: { user } - } = getRequestEvent(); - if (!user) error(401, { message: m.errors_unauthenticated() }); - return { encrypted: encrypt(data, env.CRYPTO_SECRET) }; - } -); +export const encryptCredential = command(v.object({ data: v.string() }), async ({ data }) => { + const { + locals: { user } + } = getRequestEvent(); + if (!user) error(401, { message: m.errors_unauthenticated() }); + return { encrypted: encrypt(data, env.CRYPTO_SECRET) }; +}); export const decryptCredential = command( v.object({ encrypted: v.string() }), diff --git a/src/lib/remotes/users.remote.ts b/src/lib/remotes/users.remote.ts index 87858de..56e03c7 100644 --- a/src/lib/remotes/users.remote.ts +++ b/src/lib/remotes/users.remote.ts @@ -10,13 +10,13 @@ import { import { auth } from '$lib/auth/server'; import { env } from '$lib/const/schema'; import { m } from '$lib/paraglide/messages'; -import { extractErrorMessage } from './utils'; import { db } from '$lib/server/db'; import { session as sessionTable, user as userTable, verification as verificationTable } from '$lib/server/db/auth-schema'; +import { extractErrorMessage } from '$lib/utils'; import { and, asc, count, desc, eq, gt, gte, inArray, like, lte, max, or, sql } from 'drizzle-orm'; type Role = 'admin' | 'user'; diff --git a/src/lib/remotes/utils.ts b/src/lib/remotes/utils.ts index 9a58635..e1d2bf1 100644 --- a/src/lib/remotes/utils.ts +++ b/src/lib/remotes/utils.ts @@ -6,7 +6,7 @@ import { decryptValue } from '$lib/server/db/custom-types'; import { getClient } from '$lib/server/nadir-agent/client'; export type AgentError = { - body?: { code?: string; message?: string; }; + body?: { code?: string; message?: string }; detail?: string; message?: string; status?: number; @@ -15,7 +15,6 @@ export type Package = { name: string; version: string }; export type PackageList = { manager: string; packages: Package[] }; - export const nadirForMachine = async (machineId: string) => { const { locals: { user } @@ -107,7 +106,9 @@ export async function* parseSseStream(response: Response): AsyncGenerator; +export interface components { + headers: never; + parameters: never; + pathItems: never; + requestBodies: never; + responses: never; + schemas: { + ApplyOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ApplyOutputBody.json + */ + readonly $schema?: string; + /** + * @description Network backend that applied the change + * @example nmcli + */ + backend: string; + /** @example eth0 */ + interface: string; + /** + * Format: int64 + * @description Seconds until auto-rollback unless confirmed + * @example 60 + */ + rollback_seconds: number; + /** + * @description Always "pending" — confirm to make permanent + * @example pending + */ + status: string; + }; + AuditListOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/AuditListOutputBody.json + */ + readonly $schema?: string; + /** @description Recorded actions, newest first */ + entries: components['schemas']['Entry'][] | null; + }; + CoreUsage: { + /** + * Format: int64 + * @description Logical core index + * @example 0 + */ + core: number; + /** + * Format: double + * @description Usage percentage (0–100) + * @example 23.4 + */ + usage_pct: number; + }; + CPUInfo: { + /** + * Format: int64 + * @description Peak current clock across all cores (instantaneous snapshot; 0 if cpufreq unavailable) + * @example 3157 + */ + current_mhz: number; + /** + * Format: int64 + * @description Number of logical CPUs (cores × threads) + * @example 16 + */ + logical_cpus: number; + /** + * Format: int64 + * @description Highest frequency (boost ceiling) + * @example 5137 + */ + max_mhz: number; + /** + * Format: int64 + * @description Lowest frequency the scaling governor can select + * @example 400 + */ + min_mhz: number; + /** + * @description CPU model name + * @example AMD Ryzen 7 7840U + */ + model: string; + }; + CreateGroupInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/CreateGroupInputBody.json + */ + readonly $schema?: string; + /** + * Format: int64 + * @description Explicit GID (omit to auto-assign) + * @example 1500 + */ + gid?: number; + /** + * @description Group name + * @example developers + */ + name: string; + /** @description Create a system group (groupadd --system) */ + system?: boolean; + }; + CreateUserInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/CreateUserInputBody.json + */ + readonly $schema?: string; + /** + * @description GECOS comment + * @example Alice Smith + */ + comment?: string; + /** @description Create the home directory (useradd -m) */ + create_home?: boolean; + /** + * @description Home directory (defaults to /home/) + * @example /home/alice + */ + home?: string; + /** + * @description Login shell + * @example /bin/bash + */ + shell?: string; + /** @description Create a system account (useradd --system) */ + system?: boolean; + /** + * @description Login name + * @example alice + */ + username: string; + }; + DiskInfo: { + /** + * @description Backing device + * @example /dev/nvme0n1p2 + */ + filesystem: string; + /** + * Format: int64 + * @description Space available to unprivileged users + * @example 256000000000 + */ + free_bytes: number; + /** @example btrfs */ + fstype: string; + /** @example / */ + mountpoint: string; + /** + * Format: int64 + * @example 512000000000 + */ + total_bytes: number; + /** + * Format: int64 + * @example 256000000000 + */ + used_bytes: number; + }; + DNSOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/DNSOutputBody.json + */ + readonly $schema?: string; + /** + * @description Nameservers from /etc/resolv.conf + * @example [ + * "1.1.1.1" + * ] + */ + servers: null | string[]; + }; + Entry: { + /** + * @description HTTP method + * @example POST + */ + method: string; + /** + * @description Target module + * @example users + */ + module: string; + /** + * @description Request path + * @example /api/users + */ + path: string; + /** + * Format: int64 + * @description HTTP response status + * @example 200 + */ + status: number; + /** + * @description When the action occurred (RFC3339, UTC) + * @example 2026-06-20T08:15:04Z + */ + time: string; + /** + * @description Who performed it + * @example alice + */ + username: string; + }; + ErrorDetail: { + /** @description Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id' */ + location?: string; + /** @description Error message text */ + message?: string; + /** @description The value at the given location */ + value?: unknown; + }; + ErrorEvent: { + message: string; + }; + ErrorModel: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ErrorModel.json + */ + readonly $schema?: string; + /** + * @description A human-readable explanation specific to this occurrence of the problem. + * @example Property foo is required but is missing. + */ + detail?: string; + /** @description Optional list of individual error details */ + errors?: components['schemas']['ErrorDetail'][] | null; + /** + * Format: uri + * @description A URI reference that identifies the specific occurrence of the problem. + * @example https://example.com/error-log/abc123 + */ + instance?: string; + /** + * Format: int64 + * @description HTTP status code + * @example 400 + */ + status?: number; + /** + * @description A short, human-readable summary of the problem type. This value should not change between occurrences of the error. + * @example Bad Request + */ + title?: string; + /** + * Format: uri + * @description A URI reference to human-readable documentation for the error. + * @default about:blank + * @example https://example.com/errors/example + */ + type: string; + }; + FstabEntry: { + /** @example UUID=1234-5678 */ + device: string; + /** + * Format: int64 + * @example 0 + */ + dump: number; + /** @example ext4 */ + fstype: string; + /** @example /mnt/data */ + mountpoint: string; + /** @example defaults */ + options: string; + /** + * Format: int64 + * @description fsck order (0 = skip) + * @example 2 + */ + pass: number; + }; + GenerateLocaleInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/GenerateLocaleInputBody.json + */ + readonly $schema?: string; + /** + * @description Locale to generate (e.g. fr_FR.UTF-8) + * @example fr_FR.UTF-8 + */ + locale: string; + }; + GPUInfo: { + /** + * @description PCI device ID (hex) + * @example 744c + */ + device_id: string; + /** + * @description Kernel driver in use + * @example amdgpu + */ + driver: string; + /** + * Format: double + * @description GPU memory controller utilization percentage (driver-dependent; 0 if unavailable) + * @example 15 + */ + mem_utilization_pct: number; + /** + * Format: int64 + * @description Total VRAM in bytes (driver-dependent; 0 if unavailable) + * @example 8589934592 + */ + memory_total_bytes: number; + /** + * Format: int64 + * @description Used VRAM in bytes (driver-dependent; 0 if unavailable) + * @example 27267072 + */ + memory_used_bytes: number; + /** + * @description GPU model name, or vendor:device hex ID if lspci unavailable + * @example AMD Radeon RX 7900 XTX + */ + model: string; + /** + * Format: double + * @description GPU compute utilization percentage (driver-dependent; 0 if unavailable) + * @example 23.5 + */ + utilization_pct: number; + /** + * @description PCI vendor ID (hex) + * @example 1002 + */ + vendor: string; + }; + Group: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/Group.json + */ + readonly $schema?: string; + /** + * Format: int64 + * @description Group ID + * @example 10 + */ + gid: number; + /** @description Supplementary members (primary-group members are not listed here) */ + members: null | string[]; + /** + * @description Group name + * @example wheel + */ + name: string; + /** @description True for system groups (gid < 1000) */ + system: boolean; + }; + HealthOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/HealthOutputBody.json + */ + readonly $schema?: string; + /** + * @description Embedded SQLite session store state + * @example ok + */ + database: string; + /** + * @description Overall health + * @example ok + */ + status: string; + /** + * @description Application version + * @example 1.0.0 + */ + version: string; + }; + HostEntry: { + /** + * @description Names mapped to the address + * @example [ + * "server", + * "server.local" + * ] + */ + hostnames: null | string[]; + /** + * @description IPv4 or IPv6 address + * @example 192.168.1.10 + */ + ip: string; + }; + HostnameBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/HostnameBody.json + */ + readonly $schema?: string; + /** + * @description System hostname + * @example server01 + */ + hostname: string; + }; + HostUpsertInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/HostUpsertInputBody.json + */ + readonly $schema?: string; + /** + * @description Names to map to the IP + * @example [ + * "server", + * "server.local" + * ] + */ + hostnames: null | string[]; + }; + IfaceConfig: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/IfaceConfig.json + */ + readonly $schema?: string; + /** + * @description IPv4 address (static only) + * @example 192.168.1.10 + */ + address?: string; + /** + * @description DNS servers for this interface (IPv4 or IPv6) + * @example [ + * "1.1.1.1", + * "8.8.8.8" + * ] + */ + dns?: null | string[]; + /** + * @description Default gateway (static only, optional) + * @example 192.168.1.1 + */ + gateway?: string; + /** @description Optional IPv6 settings. Omit to leave IPv6 untouched; include to manage it. */ + ipv6?: components['schemas']['IPv6Config']; + /** + * @description "static" for a fixed address, "dhcp" for automatic + * @example static + * @enum {string} + */ + method: 'dhcp' | 'static'; + /** + * Format: int64 + * @description Network prefix length (static only) + * @example 24 + */ + prefix?: number; + /** + * Format: int64 + * @description Auto-revert after this many seconds unless confirmed. 0 uses the default (60s). + * @example 60 + */ + rollback_seconds?: number; + /** @description Static routes to install for this interface */ + routes?: components['schemas']['Route'][] | null; + }; + InstallInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/InstallInputBody.json + */ + readonly $schema?: string; + /** + * @description Package to install + * @example htop + */ + name: string; + }; + Interface: { + /** + * @description IPv4 addresses in CIDR form + * @example [ + * "192.168.1.10/24" + * ] + */ + ipv4: null | string[]; + /** + * @description IPv6 addresses in CIDR form + * @example [ + * "fe80::1/64" + * ] + */ + ipv6: null | string[]; + /** @example 52:54:00:12:34:56 */ + mac: string; + /** + * Format: int64 + * @example 1500 + */ + mtu: number; + /** @example eth0 */ + name: string; + /** + * @description operstate: up / down / unknown + * @example up + */ + state: string; + }; + IPv6Config: { + /** + * @description IPv6 address (static only) + * @example 2001:db8::10 + */ + address?: string; + /** + * @description IPv6 default gateway (static only, optional) + * @example 2001:db8::1 + */ + gateway?: string; + /** + * @description "auto" (SLAAC), "static", or "ignore" (disable IPv6) + * @example static + * @enum {string} + */ + method: 'auto' | 'ignore' | 'static'; + /** + * Format: int64 + * @description Prefix length (static only) + * @example 64 + */ + prefix?: number; + }; + KeymapsOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/KeymapsOutputBody.json + */ + readonly $schema?: string; + /** @description Available virtual console keymaps */ + keymaps: null | string[]; + /** @description When keymaps is empty, why: e.g. "kbd not installed on this server" */ + reason?: string; + }; + ListFstabOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListFstabOutputBody.json + */ + readonly $schema?: string; + entries: components['schemas']['FstabEntry'][] | null; + }; + ListGroupsOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListGroupsOutputBody.json + */ + readonly $schema?: string; + /** @description All groups from /etc/group */ + groups: components['schemas']['Group'][] | null; + }; + ListHostsOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListHostsOutputBody.json + */ + readonly $schema?: string; + entries: components['schemas']['HostEntry'][] | null; + }; + ListInterfacesOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListInterfacesOutputBody.json + */ + readonly $schema?: string; + interfaces: components['schemas']['Interface'][] | null; + }; + ListMountsOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListMountsOutputBody.json + */ + readonly $schema?: string; + mounts: components['schemas']['Mount'][] | null; + }; + ListOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListOutputBody.json + */ + readonly $schema?: string; + /** + * @description Detected package manager + * @example dnf + */ + manager: string; + packages: components['schemas']['Package'][] | null; + }; + ListRoutesOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListRoutesOutputBody.json + */ + readonly $schema?: string; + routes: components['schemas']['RouteEntry'][] | null; + }; + ListServicesOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListServicesOutputBody.json + */ + readonly $schema?: string; + /** @description All service units, active and inactive */ + services: components['schemas']['ServiceUnit'][] | null; + }; + ListUsersOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ListUsersOutputBody.json + */ + readonly $schema?: string; + /** @description All accounts from /etc/passwd */ + users: components['schemas']['User'][] | null; + }; + LoadInfo: { + /** @description Per-core CPU usage percentage (sampled over ~1 s); empty until the first sample completes */ + cpu_usage: components['schemas']['CoreUsage'][] | null; + /** + * Format: double + * @example 0.42 + */ + load1: number; + /** + * Format: double + * @example 0.61 + */ + load15: number; + /** + * Format: double + * @example 0.55 + */ + load5: number; + }; + LocalesOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/LocalesOutputBody.json + */ + readonly $schema?: string; + /** @description Available locales */ + locales: null | string[]; + }; + LocaleStatusBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/LocaleStatusBody.json + */ + readonly $schema?: string; + /** + * @description System locale (LANG) + * @example it_IT.UTF-8 + */ + lang: string; + /** + * @description Fallback language list (LANGUAGE) + * @example en_US: + */ + language: string; + /** + * @description Virtual console keymap + * @example it + */ + vc_keymap: string; + /** + * @description X11 keyboard layout + * @example it + */ + x11_layout: string; + }; + LogEntry: { + /** @example Started OpenSSH server daemon. */ + message: string; + /** + * Format: int64 + * @description syslog priority 0 (emerg) – 7 (debug); 6 for file lines + * @example 6 + */ + priority: number; + /** + * @description Record timestamp (RFC3339, UTC); empty for file lines + * @example 2026-06-20T08:15:04Z + */ + time: string; + }; + LoginInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/LoginInputBody.json + */ + readonly $schema?: string; + /** @description System password */ + password: string; + /** @description System username */ + username: string; + }; + LoginOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/LoginOutputBody.json + */ + readonly $schema?: string; + /** @example logged in */ + status: string; + }; + LogoutOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/LogoutOutputBody.json + */ + readonly $schema?: string; + /** @example logged out */ + status: string; + }; + LogsOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/LogsOutputBody.json + */ + readonly $schema?: string; + /** @description Log records, oldest first */ + entries: components['schemas']['LogEntry'][] | null; + }; + MemoryInfo: { + /** + * Format: int64 + * @description Memory available for new allocations without swapping + * @example 8192000000 + */ + available_bytes: number; + /** + * Format: int64 + * @example 8589934592 + */ + swap_free_bytes: number; + /** + * Format: int64 + * @example 8589934592 + */ + swap_total_bytes: number; + /** + * Format: int64 + * @example 16384000000 + */ + total_bytes: number; + /** + * Format: int64 + * @description total - available + * @example 8192000000 + */ + used_bytes: number; + }; + ModuleInfo: { + /** + * @description Stable module identifier + * @example system + */ + id: string; + /** + * @description Human-readable module name + * @example System + */ + name: string; + /** @description Permissions this module exposes (never includes the "*" wildcard) */ + permissions: null | string[]; + }; + ModulesOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ModulesOutputBody.json + */ + readonly $schema?: string; + /** @description Registered modules, sorted by ID */ + modules: components['schemas']['ModuleInfo'][] | null; + }; + Mount: { + /** @example /dev/sda1 */ + device: string; + /** @example ext4 */ + fstype: string; + /** @example /mnt/data */ + mountpoint: string; + /** @example rw,relatime */ + options: string; + }; + MountInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/MountInputBody.json + */ + readonly $schema?: string; + /** + * @description Block device or UUID=/LABEL= specifier + * @example /dev/sdb1 + */ + device: string; + /** Format: int64 */ + dump?: number; + /** @example ext4 */ + fstype: string; + /** + * @description Absolute mount path + * @example /mnt/data + */ + mountpoint: string; + /** + * @description Mount options (default: defaults) + * @example defaults + */ + options?: string; + /** + * Format: int64 + * @description fsck order (default 2 for real filesystems, 0 to skip) + */ + pass?: number; + }; + NetInterface: { + /** @description Assigned addresses in CIDR notation */ + addresses: null | string[]; + /** @example aa:bb:cc:dd:ee:ff */ + mac: string; + /** @example eth0 */ + name: string; + /** @description Interface is administratively up */ + up: boolean; + }; + OSInfo: { + /** + * @description Machine hardware architecture (uname -m) + * @example x86_64 + */ + architecture: string; + /** + * @description System hostname + * @example server01 + */ + hostname: string; + /** + * @description Running kernel release (uname -r) + * @example 7.0.12-201.fc44.x86_64 + */ + kernel: string; + /** + * @description Distro name from /etc/os-release PRETTY_NAME + * @example Fedora Linux 44 (Workstation Edition) + */ + pretty_name: string; + }; + Package: { + /** + * @description Package name + * @example openssh-server + */ + name: string; + /** + * @description Installed version, or the available version for updates + * @example 9.6p1 + */ + version: string; + }; + PendingInfo: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/PendingInfo.json + */ + readonly $schema?: string; + /** + * @description Interface with a pending change + * @example eth0 + */ + interface: string; + /** + * Format: int64 + * @description Seconds until auto-rollback + * @example 45 + */ + seconds_remaining: number; + }; + PkgDoneEvent: { + /** @description Exit error when it failed */ + error?: string; + /** @description True if the package manager exited 0 */ + success: boolean; + }; + PkgErrorEvent: { + message: string; + }; + PkgOutputEvent: { + /** @description One line of the package manager's terminal output */ + line: string; + }; + PowerInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/PowerInputBody.json + */ + readonly $schema?: string; + /** + * @description When to act, as a shutdown(8) TIME (e.g. "+5" minutes, "23:00"). Empty or 'now' = immediate. + * @example +1 + */ + when?: string; + }; + Route: { + /** + * @description Destination network in CIDR notation, or "default" + * @example 10.0.0.0/24 + */ + destination: string; + /** + * @description Next-hop gateway + * @example 192.168.1.1 + */ + gateway: string; + }; + RouteEntry: { + /** + * @description Destination network, or "default" + * @example default + */ + destination: string; + /** @example 192.168.1.1 */ + gateway?: string; + /** @example eth0 */ + interface: string; + /** + * Format: int64 + * @example 100 + */ + metric?: number; + /** + * @description Preferred source address + * @example 192.168.1.10 + */ + source?: string; + }; + ServiceStatusBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/ServiceStatusBody.json + */ + readonly $schema?: string; + /** + * @description active / inactive / failed + * @example active + */ + active_state: string; + /** @example OpenSSH server daemon */ + description: string; + /** + * @description loaded / not-found / masked + * @example loaded + */ + load_state: string; + /** @example running */ + sub_state: string; + /** @example sshd.service */ + unit: string; + /** + * @description enabled / disabled / static + * @example enabled + */ + unit_file_state: string; + }; + ServiceUnit: { + /** + * @description High-level active state + * @example active + */ + active: string; + /** + * @description Unit description + * @example OpenSSH server daemon + */ + description: string; + /** + * @description Load state + * @example loaded + */ + load: string; + /** + * @description Low-level sub state + * @example running + */ + sub: string; + /** + * @description Unit name + * @example sshd.service + */ + unit: string; + }; + SetGroupsInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SetGroupsInputBody.json + */ + readonly $schema?: string; + /** @description Supplementary groups; replaces the user's full supplementary set */ + groups: null | string[]; + }; + SetKeymapInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SetKeymapInputBody.json + */ + readonly $schema?: string; + /** + * @description Virtual console keymap + * @example it + */ + keymap: string; + }; + SetLocaleInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SetLocaleInputBody.json + */ + readonly $schema?: string; + /** + * @description Locale to set as LANG + * @example it_IT.UTF-8 + */ + lang: string; + /** + * @description Fallback language list (LANGUAGE) + * @example en_US: + */ + language?: string; + }; + SetNTPInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SetNTPInputBody.json + */ + readonly $schema?: string; + /** @description Enable network time synchronization */ + enabled: boolean; + }; + SetPasswordInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SetPasswordInputBody.json + */ + readonly $schema?: string; + /** @description New password (sent to chpasswd over stdin, never argv) */ + password: string; + }; + SetTimeInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SetTimeInputBody.json + */ + readonly $schema?: string; + /** + * @description New time (RFC3339). Requires NTP disabled. + * @example 2026-06-19T13:36:31Z + */ + time: string; + }; + SetTimezoneInputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SetTimezoneInputBody.json + */ + readonly $schema?: string; + /** + * @description IANA timezone name + * @example Europe/Rome + */ + timezone: string; + }; + StatusOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/StatusOutputBody.json + */ + readonly $schema?: string; + /** + * @description Always "ok" on success + * @example ok + */ + status: string; + }; + SystemInfoBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/SystemInfoBody.json + */ + readonly $schema?: string; + /** + * @description Boot time (RFC3339, UTC) + * @example 2026-06-19T12:08:00Z + */ + boot_time: string; + /** @description Processor model and core count */ + cpu: components['schemas']['CPUInfo']; + /** @description Mounted block-device filesystems */ + disks: components['schemas']['DiskInfo'][] | null; + /** @description Graphics processors detected via DRM sysfs */ + gpus: components['schemas']['GPUInfo'][] | null; + /** @description Load averages (1/5/15 min) */ + load: components['schemas']['LoadInfo']; + /** @description RAM and swap usage in bytes */ + memory: components['schemas']['MemoryInfo']; + /** @description Network interfaces and their addresses */ + network_interfaces: components['schemas']['NetInterface'][] | null; + /** @description OS and kernel identity */ + os: components['schemas']['OSInfo']; + /** @description Thermal sensor readings in Celsius */ + temperatures: components['schemas']['Temperature'][] | null; + /** + * Format: int64 + * @description Seconds since boot + * @example 12490 + */ + uptime_seconds: number; + }; + Temperature: { + /** + * Format: double + * @example 47.5 + */ + celsius: number; + /** + * @description hwmon chip name; identifies the source (k10temp/coretemp=CPU, amdgpu/nvidia=GPU, nvme=disk) + * @example k10temp + */ + chip: string; + /** + * @description Per-sensor label, or the chip name when the sensor is unlabelled + * @example Tctl + */ + label: string; + }; + TimeStatusBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/TimeStatusBody.json + */ + readonly $schema?: string; + /** @description An NTP service is available on this host */ + can_ntp: boolean; + /** @description Hardware clock kept in local time instead of UTC */ + local_rtc: boolean; + /** @description Network time synchronization enabled */ + ntp: boolean; + /** @description Clock is currently synchronized */ + ntp_synchronized: boolean; + /** + * @description Current system time (RFC3339, UTC) + * @example 2026-06-19T13:36:31Z + */ + time: string; + /** + * @description IANA timezone name + * @example Europe/Rome + */ + timezone: string; + }; + TimezonesOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/TimezonesOutputBody.json + */ + readonly $schema?: string; + /** @description Available IANA timezone names */ + timezones: null | string[]; + }; + User: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/User.json + */ + readonly $schema?: string; + /** + * @description GECOS comment (often the full name) + * @example Alice Smith + */ + comment: string; + /** + * Format: int64 + * @description Primary group ID + * @example 1000 + */ + gid: number; + /** + * @description Home directory + * @example /home/alice + */ + home: string; + /** + * @description Login shell + * @example /bin/bash + */ + shell: string; + /** @description True for system accounts (uid < 1000) */ + system: boolean; + /** + * Format: int64 + * @description User ID + * @example 1000 + */ + uid: number; + /** + * @description Login name + * @example alice + */ + username: string; + }; + UserGroupsOutputBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/UserGroupsOutputBody.json + */ + readonly $schema?: string; + /** @description All groups the user belongs to (primary + supplementary) */ + groups: null | string[]; + /** @example alice */ + username: string; + }; + WhoamiBody: { + /** + * Format: uri + * @description A URL to the JSON Schema for this object. + * @example https://example.com/schemas/WhoamiBody.json + */ + readonly $schema?: string; + /** @description Module ID -> permissions the caller holds. Modules where they hold none are omitted. */ + permissions: { + [key: string]: null | string[]; + }; + /** + * @description Authenticated username + * @example urania + */ + username: string; + }; + }; +} +export interface operations { + 'audit-list': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: { + /** @description Max entries to return, newest first */ + limit?: number; + }; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['AuditListOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'groups-create': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['CreateGroupInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['Group']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'groups-delete': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Group name */ + group: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'groups-get': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Group name */ + group: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['Group']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'groups-list': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListGroupsOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + health: { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['HealthOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Service Unavailable */ + 503: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'list-modules': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ModulesOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Error */ + default: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + login: { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['LoginInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['LoginOutputBody']; + }; + headers: { + [name: string]: unknown; + 'Set-Cookie'?: string; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Too Many Requests */ + 429: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + logout: { + parameters: { + cookie?: { + nadir_session_id?: string; + }; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['LogoutOutputBody']; + }; + headers: { + [name: string]: unknown; + 'Set-Cookie'?: string; + }; + }; + /** @description Error */ + default: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'meta-update': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description Accepted */ + 202: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-apply-config': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Interface name */ + name: string; + }; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['IfaceConfig']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ApplyOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Implemented */ + 501: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-confirm-change': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Interface name */ + name: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Implemented */ + 501: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-delete-host': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description IP address whose entry to remove */ + ip: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-get-dns': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['DNSOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-get-interface': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Interface name */ + name: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['IfaceConfig']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-get-pending': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['PendingInfo']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-link-down': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Interface name */ + name: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ApplyOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Implemented */ + 501: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-link-up': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Interface name */ + name: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Implemented */ + 501: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-list-hosts': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListHostsOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-list-interfaces': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListInterfacesOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-list-routes': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListRoutesOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-rollback-change': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Interface name */ + name: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Implemented */ + 501: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'networking-upsert-host': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description IP address to add or update */ + ip: string; + }; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['HostUpsertInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Implemented */ + 501: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'packages-install': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['InstallInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'text/event-stream': ( + | { + data: components['schemas']['PkgDoneEvent']; + /** + * @description The event name. + * @constant + */ + event: 'done'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgErrorEvent']; + /** + * @description The event name. + * @constant + */ + event: 'error'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgOutputEvent']; + /** + * @description The event name. + * @constant + */ + event: 'output'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + )[]; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Error */ + default: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'packages-list-installed': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'packages-list-updates': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'packages-remove': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Package to remove */ + name: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'text/event-stream': ( + | { + data: components['schemas']['PkgDoneEvent']; + /** + * @description The event name. + * @constant + */ + event: 'done'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgErrorEvent']; + /** + * @description The event name. + * @constant + */ + event: 'error'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgOutputEvent']; + /** + * @description The event name. + * @constant + */ + event: 'output'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + )[]; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Error */ + default: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'packages-upgrade': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'text/event-stream': ( + | { + data: components['schemas']['PkgDoneEvent']; + /** + * @description The event name. + * @constant + */ + event: 'done'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgErrorEvent']; + /** + * @description The event name. + * @constant + */ + event: 'error'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgOutputEvent']; + /** + * @description The event name. + * @constant + */ + event: 'output'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + )[]; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Error */ + default: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'packages-upgrade-one': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Package to upgrade */ + name: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'text/event-stream': ( + | { + data: components['schemas']['PkgDoneEvent']; + /** + * @description The event name. + * @constant + */ + event: 'done'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgErrorEvent']; + /** + * @description The event name. + * @constant + */ + event: 'error'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['PkgOutputEvent']; + /** + * @description The event name. + * @constant + */ + event: 'output'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + )[]; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Error */ + default: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-disable': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description systemd unit name */ + unit: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-enable': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description systemd unit name */ + unit: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-get': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description systemd unit name */ + unit: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ServiceStatusBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-list': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListServicesOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-logs': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Unit name as listed by GET /api/services; the trailing .service is optional */ + unit: string; + }; + query?: { + /** @description How many recent records to return (max 10000) */ + lines?: number; + /** @description Log file (file source only); must be allowlisted for this unit in config */ + path?: string; + /** @description Max syslog priority to include: 0 emerg .. 7 debug (journal source only). 7 = all. */ + priority?: number; + /** @description journalctl time filter (journal source only) */ + since?: string; + /** @description Where to read logs from */ + source?: 'file' | 'journal'; + }; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['LogsOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-logs-stream': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Unit name as listed by GET /api/services; the trailing .service is optional */ + unit: string; + }; + query?: { + /** @description Log file (file source only); must be allowlisted for this unit in config */ + path?: string; + /** @description Max syslog priority to include: 0 emerg .. 7 debug (journal source only). 7 = all. */ + priority?: number; + /** @description Backfill window (journal source only) */ + since?: string; + /** @description Where to stream logs from */ + source?: 'file' | 'journal'; + }; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'text/event-stream': ( + | { + data: components['schemas']['ErrorEvent']; + /** + * @description The event name. + * @constant + */ + event: 'error'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + | { + data: components['schemas']['LogEntry']; + /** + * @description The event name. + * @constant + */ + event: 'log'; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } + )[]; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Error */ + default: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-restart': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description systemd unit name */ + unit: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-start': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description systemd unit name */ + unit: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'services-stop': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description systemd unit name */ + unit: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'storage-add-mount': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['MountInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'storage-list-fstab': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListFstabOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'storage-list-mounts': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListMountsOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'storage-remove-mount': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: { + /** @description Mountpoint to unmount and remove from fstab */ + mountpoint?: string; + }; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-generate-locale': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['GenerateLocaleInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Implemented */ + 501: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-get-hostname': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['HostnameBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-get-info': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['SystemInfoBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-get-locale': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['LocaleStatusBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-get-time': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['TimeStatusBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-list-keymaps': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['KeymapsOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-list-locales': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['LocalesOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-list-timezones': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['TimezonesOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-poweroff': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['PowerInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-reboot': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['PowerInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-set-hostname': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['HostnameBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-set-keymap': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['SetKeymapInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-set-locale': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['SetLocaleInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-set-ntp': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['SetNTPInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['TimeStatusBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-set-time': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['SetTimeInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'system-set-timezone': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['SetTimezoneInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'users-create': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['CreateUserInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['User']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'users-delete': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Login name */ + username: string; + }; + query?: { + /** @description Also remove the home directory and mail spool (userdel -r) */ + remove_home?: boolean; + }; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'users-get': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Login name */ + username: string; + }; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['User']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'users-list': { + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['ListUsersOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'users-set-groups': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Login name */ + username: string; + }; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['SetGroupsInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['UserGroupsOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Conflict */ + 409: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + 'users-set-password': { + parameters: { + cookie?: never; + header?: never; + path: { + /** @description Login name */ + username: string; + }; + query?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['SetPasswordInputBody']; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['StatusOutputBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Bad Request */ + 400: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Forbidden */ + 403: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Not Found */ + 404: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; + whoami: { + parameters: { + cookie?: { + nadir_session_id?: string; + }; + header?: { + Authorization?: string; + }; + path?: never; + query?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + content: { + 'application/json': components['schemas']['WhoamiBody']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Unprocessable Entity */ + 422: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Too Many Requests */ + 429: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + /** @description Internal Server Error */ + 500: { + content: { + 'application/problem+json': components['schemas']['ErrorModel']; + }; + headers: { + [name: string]: unknown; + }; + }; + }; + }; +} export interface paths { - "/api/_modules": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List registered modules - * @description Returns every registered module with its ID, display name, and exported permissions. Public (same static shape as /openapi.json); used by the frontend for navigation and the role/permission matrix. - */ - get: operations["list-modules"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/audit": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List recorded actions - * @description Returns the audit trail of privileged write operations (who, what, when, result), newest first. - */ - get: operations["audit-list"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/groups": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List groups - * @description Returns every group in /etc/group, including system groups (flagged via `system`). - */ - get: operations["groups-list"]; - put?: never; - /** - * Create a group - * @description Creates a group via groupadd. 409 if the group already exists. - */ - post: operations["groups-create"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/groups/{group}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get a single group - * @description Returns one group by name. 404 if it does not exist. - */ - get: operations["groups-get"]; - put?: never; - post?: never; - /** - * Delete a group - * @description Removes a group via groupdel. Returns 409 if it is the primary group of an existing user (groupdel refuses), 404 if it does not exist. - */ - delete: operations["groups-delete"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/health": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Health check - * @description Public liveness/readiness probe. Reports whether the embedded SQLite session store is reachable. Returns 503 when it is not. - */ - get: operations["health"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/login": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Authenticate and start a session - * @description Verifies the username and password against PAM (the dedicated nadir service) and, on success, sets an HttpOnly session cookie used to authorize all other endpoints. - */ - post: operations["login"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/logout": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * End the current session - * @description Invalidates the session named by the cookie and clears it. Always succeeds, even without a valid session. - */ - post: operations["logout"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/dns": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get configured DNS servers - * @description Returns the nameservers listed in /etc/resolv.conf. DNS is set per-interface as part of the interface config (PUT /api/networking/interfaces/{name}), so there is no standalone DNS write endpoint. - */ - get: operations["networking-get-dns"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/hosts": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List /etc/hosts entries - * @description Returns the static host-to-address mappings from /etc/hosts. - */ - get: operations["networking-list-hosts"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/hosts/{ip}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - /** - * Add or update a /etc/hosts entry - * @description Sets the hostnames for an IP. Replaces the existing line for that IP, or appends a new one; all other lines (comments included) are left untouched. - */ - put: operations["networking-upsert-host"]; - post?: never; - /** - * Remove a /etc/hosts entry - * @description Removes the line(s) mapping the given IP. 404 if no entry exists for it. - */ - delete: operations["networking-delete-host"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/interfaces": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List network interfaces - * @description Returns every interface with its state, MAC, MTU and addresses, via `ip -j addr`. - */ - get: operations["networking-list-interfaces"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/interfaces/{name}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get an interface's current configuration - * @description Returns the IfaceConfig the backend currently has for this interface (method, address/prefix, gateway, DNS, IPv6). Same schema as PUT /api/networking/interfaces/{name}, so the frontend can prefill an edit form from this response directly. Returns 501 when no backend was detected (nmcli / networkd / ifupdown). - */ - get: operations["networking-get-interface"]; - /** - * Apply interface configuration - * @description Replaces the interface's IPv4 configuration. The change is applied immediately but starts a rollback timer — if not confirmed within the timeout (default 60s), the prior configuration is automatically restored. This prevents lock-yourself-out mistakes on remote hosts. - */ - put: operations["networking-apply-config"]; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/interfaces/{name}/confirm": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Confirm a pending change - * @description Cancels the rollback timer, making the applied configuration permanent. - */ - post: operations["networking-confirm-change"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/interfaces/{name}/down": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Take an interface down - * @description Brings the interface down behind the rollback safety net: it is brought back up automatically if not confirmed within the timeout (default 60s). This prevents taking down the link you're managing the host over and losing access. - */ - post: operations["networking-link-down"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/interfaces/{name}/rollback": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Immediately revert a pending change - * @description Reverts the interface to its prior configuration and clears the pending change. - */ - post: operations["networking-rollback-change"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/interfaces/{name}/up": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Bring an interface up */ - post: operations["networking-link-up"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/pending": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get pending change status - * @description Returns the currently pending change (interface name and seconds until auto-rollback), or 404 if there is no pending change. - */ - get: operations["networking-get-pending"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/networking/routes": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List the IPv4 route table - * @description Returns the kernel IPv4 route table via `ip -j route`. - */ - get: operations["networking-list-routes"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/packages": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** List installed packages */ - get: operations["packages-list-installed"]; - put?: never; - /** - * Install a package (streamed) - * @description Installs a package, streaming the package manager's output as `output` events and ending with a `done` event carrying the exit status. - */ - post: operations["packages-install"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/packages/updates": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** List available updates */ - get: operations["packages-list-updates"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/packages/upgrade": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Upgrade all packages (streamed) - * @description Upgrades every package to its latest version, streaming the package manager's output live. - */ - post: operations["packages-upgrade"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/packages/upgrade/{name}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Upgrade a single package (streamed) - * @description Upgrades the named package to its latest version, streaming the package manager's output live. apt uses `install --only-upgrade` so the package must already be installed; dnf/pacman handle this natively. - */ - post: operations["packages-upgrade-one"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/packages/{name}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - /** Remove a package (streamed) */ - delete: operations["packages-remove"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List service units - * @description Returns all service units (active and inactive) via `systemctl list-units --type=service --all`. - */ - get: operations["services-list"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get a service's status - * @description Returns load/active/sub/unit-file state for one unit via `systemctl show`. Returns 404 when the unit does not exist. - */ - get: operations["services-get"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}/disable": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Disable a service at boot - * @description Disables the unit (`systemctl disable`). Returns 404 when the unit does not exist. - */ - post: operations["services-disable"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}/enable": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Enable a service at boot - * @description Enables the unit (`systemctl enable`). Returns 404 when the unit does not exist. - */ - post: operations["services-enable"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}/logs": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get recent log records for a service - * @description Returns a snapshot of the unit's logs from the journal (default) or an allowlisted file (source=file&path=). Use /logs/stream to follow new records live. - */ - get: operations["services-logs"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}/logs/stream": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Stream a service's logs (Server-Sent Events) - * @description Follows the unit's journal (journalctl -f) or an allowlisted file (source=file&path=, via tail -F) and emits a `log` event per record. Stops when the client disconnects. - */ - get: operations["services-logs-stream"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}/restart": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Restart a service - * @description Restarts the unit (`systemctl restart`). Returns 404 when the unit does not exist. - */ - post: operations["services-restart"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}/start": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Start a service - * @description Starts the unit (`systemctl start`). Returns 404 when the unit does not exist. - */ - post: operations["services-start"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{unit}/stop": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Stop a service - * @description Stops the unit (`systemctl stop`). Returns 404 when the unit does not exist. - */ - post: operations["services-stop"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/storage/fstab": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List /etc/fstab entries - * @description Returns the persistent mount definitions from /etc/fstab. - */ - get: operations["storage-list-fstab"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/storage/mounts": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List active mounts - * @description Returns the kernel mount table (/proc/mounts). - */ - get: operations["storage-list-mounts"]; - put?: never; - /** - * Add and mount a filesystem - * @description Appends an /etc/fstab entry and mounts it. If the mount fails the fstab entry is rolled back, so a bad request leaves the system unchanged. - */ - post: operations["storage-add-mount"]; - /** - * Unmount and remove a filesystem - * @description Unmounts the filesystem (if mounted) and removes its /etc/fstab entry. The mountpoint is passed as a query parameter since it contains slashes. - */ - delete: operations["storage-remove-mount"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/hostname": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get system hostname - * @description Returns the current hostname as reported by hostnamectl. - */ - get: operations["system-get-hostname"]; - put?: never; - /** - * Set system hostname - * @description Sets the static hostname via hostnamectl, which owns /etc/hostname and manages the static/pretty/transient names. - */ - post: operations["system-set-hostname"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/info": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get system information - * @description Returns an overview for a dashboard: OS/kernel identity, CPU, memory and swap, mounted disks, load averages, uptime, network interfaces, and temperatures. All values come from cheap local reads (/proc, /sys, syscalls) with no D-Bus dependency; each section is best-effort. - */ - get: operations["system-get-info"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/keymap": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Set virtual console keymap - * @description Sets the virtual console keymap via localectl. The value is validated against the host's keymap list, so an unknown keymap returns 400. - */ - post: operations["system-set-keymap"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/keymaps": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List available console keymaps - * @description Returns every virtual console keymap known to the host. - */ - get: operations["system-list-keymaps"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/locale": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get locale and keyboard layout - * @description Returns the system locale (LANG), virtual console keymap, and X11 layout from localectl. - */ - get: operations["system-get-locale"]; - put?: never; - /** - * Set system locale (LANG) - * @description Sets LANG via localectl. The value is validated against the host's locale list, so an unknown locale returns 400. - */ - post: operations["system-set-locale"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/locale/generate": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Generate (install) a new locale - * @description Generates a locale so it becomes available for use with set-locale. On Debian/Ubuntu/Arch this uncomments the entry in /etc/locale.gen and runs locale-gen; on RHEL/Fedora it uses localedef. Idempotent: if the locale is already generated, returns 200 immediately. - */ - post: operations["system-generate-locale"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/locales": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List available locales - * @description Returns every locale the host can be configured to use, suitable for populating a selector. - */ - get: operations["system-list-locales"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/ntp": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Enable or disable time synchronization - * @description Toggles network time synchronization via `timedatectl set-ntp`. Selecting specific NTP servers is not yet supported. Returns the resulting time status: on enable, `ntp` is true immediately, but `ntp_synchronized` stays false until the NTP daemon converges (seconds to minutes). - */ - post: operations["system-set-ntp"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/poweroff": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Power off the system - * @description Requires the `root` permission. The response is sent once `shutdown` accepts the request; for the immediate form it returns before the machine actually goes down, so a 200 does not guarantee a clean shutdown completed. - */ - post: operations["system-poweroff"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/reboot": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Reboot the system - * @description Requires the `root` permission. The response is sent once `shutdown` accepts the request; for the immediate form it returns before the machine actually goes down, so a 200 does not guarantee a clean shutdown completed. - */ - post: operations["system-reboot"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/time": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get time, timezone and sync status - * @description Returns the current UTC time plus timezone and synchronization state from timedatectl. - */ - get: operations["system-get-time"]; - put?: never; - /** - * Set system time manually - * @description Sets the clock to an explicit RFC3339 time. This only works when NTP is disabled; otherwise timedatectl refuses and the call returns 409. - */ - post: operations["system-set-time"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/timezone": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Set system timezone - * @description Sets the timezone via timedatectl. The value is validated against the host's timezone list, so an unknown name returns 400. - */ - post: operations["system-set-timezone"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/system/timezones": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List available timezones - * @description Returns every IANA timezone name known to the host, suitable for populating a selector. - */ - get: operations["system-list-timezones"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/terminal": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Connect to an interactive terminal - * @description Upgrades the connection to a WebSocket and spawns a PTY shell as the logged-in user. Send JSON `{cols, rows}` text messages to resize, and raw binary/text messages for stdin. This is a raw WebSocket endpoint — it cannot be exercised from the API docs "Try it" panel; use a WebSocket client. - */ - get: operations["terminal-connect"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/update": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Update nadir to the latest release - * @description Equivalent to running `sudo nadir update` on the host: queries server.release_repo for the latest release, downloads the binary matching the host's architecture, atomically replaces the running binary, and restarts the systemd unit. Returns 202 immediately; the service restart drops in-flight connections, so poll /api/health to confirm the new version is up. Requires the wildcard admin role. - */ - post: operations["meta-update"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/users": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List user accounts - * @description Returns every account in /etc/passwd, including system accounts (flagged via `system`). - */ - get: operations["users-list"]; - put?: never; - /** - * Create a user account - * @description Creates an account via useradd. The new account has a locked password until one is set via the password endpoint. 409 if the user already exists. - */ - post: operations["users-create"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/users/{username}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get a single user - * @description Returns one account by login name. 404 if it does not exist. - */ - get: operations["users-get"]; - put?: never; - post?: never; - /** - * Delete a user account - * @description Removes an account via userdel. Pass ?remove_home=true to also delete the home directory. 404 if the user does not exist. - */ - delete: operations["users-delete"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/users/{username}/groups": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - /** - * Set a user's supplementary groups - * @description Replaces the user's full supplementary group set via `usermod -G` (an empty list removes them from all supplementary groups). Returns the resulting group membership. 404 if the user does not exist; 400 if any named group is missing. Requires the `root` permission: adding an account to wheel/sudo/docker is a privilege grant, not a routine write. - */ - put: operations["users-set-groups"]; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/users/{username}/password": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Set a user's password - * @description Sets the password via chpasswd (fed over stdin, so the secret never appears in the process list). 404 if the user does not exist. Requires the `root` permission: resetting a privileged account's password (e.g. root) is a full-system action, not a routine write. - */ - post: operations["users-set-password"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/whoami": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get the current user and their permissions - * @description Returns the authenticated username and, per module, the permissions the caller holds (wildcards resolved). Pair with /api/_modules to render the full permission matrix. - */ - get: operations["whoami"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; + '/api/_modules': { + delete?: never; + /** + * List registered modules + * @description Returns every registered module with its ID, display name, and exported permissions. Public (same static shape as /openapi.json); used by the frontend for navigation and the role/permission matrix. + */ + get: operations['list-modules']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/audit': { + delete?: never; + /** + * List recorded actions + * @description Returns the audit trail of privileged write operations (who, what, when, result), newest first. + */ + get: operations['audit-list']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/groups': { + delete?: never; + /** + * List groups + * @description Returns every group in /etc/group, including system groups (flagged via `system`). + */ + get: operations['groups-list']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Create a group + * @description Creates a group via groupadd. 409 if the group already exists. + */ + post: operations['groups-create']; + put?: never; + trace?: never; + }; + '/api/groups/{group}': { + /** + * Delete a group + * @description Removes a group via groupdel. Returns 409 if it is the primary group of an existing user (groupdel refuses), 404 if it does not exist. + */ + delete: operations['groups-delete']; + /** + * Get a single group + * @description Returns one group by name. 404 if it does not exist. + */ + get: operations['groups-get']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/health': { + delete?: never; + /** + * Health check + * @description Public liveness/readiness probe. Reports whether the embedded SQLite session store is reachable. Returns 503 when it is not. + */ + get: operations['health']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/login': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Authenticate and start a session + * @description Verifies the username and password against PAM (the dedicated nadir service) and, on success, sets an HttpOnly session cookie used to authorize all other endpoints. + */ + post: operations['login']; + put?: never; + trace?: never; + }; + '/api/logout': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * End the current session + * @description Invalidates the session named by the cookie and clears it. Always succeeds, even without a valid session. + */ + post: operations['logout']; + put?: never; + trace?: never; + }; + '/api/networking/dns': { + delete?: never; + /** + * Get configured DNS servers + * @description Returns the nameservers listed in /etc/resolv.conf. DNS is set per-interface as part of the interface config (PUT /api/networking/interfaces/{name}), so there is no standalone DNS write endpoint. + */ + get: operations['networking-get-dns']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/networking/hosts': { + delete?: never; + /** + * List /etc/hosts entries + * @description Returns the static host-to-address mappings from /etc/hosts. + */ + get: operations['networking-list-hosts']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/networking/hosts/{ip}': { + /** + * Remove a /etc/hosts entry + * @description Removes the line(s) mapping the given IP. 404 if no entry exists for it. + */ + delete: operations['networking-delete-host']; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + /** + * Add or update a /etc/hosts entry + * @description Sets the hostnames for an IP. Replaces the existing line for that IP, or appends a new one; all other lines (comments included) are left untouched. + */ + put: operations['networking-upsert-host']; + trace?: never; + }; + '/api/networking/interfaces': { + delete?: never; + /** + * List network interfaces + * @description Returns every interface with its state, MAC, MTU and addresses, via `ip -j addr`. + */ + get: operations['networking-list-interfaces']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/networking/interfaces/{name}': { + delete?: never; + /** + * Get an interface's current configuration + * @description Returns the IfaceConfig the backend currently has for this interface (method, address/prefix, gateway, DNS, IPv6). Same schema as PUT /api/networking/interfaces/{name}, so the frontend can prefill an edit form from this response directly. Returns 501 when no backend was detected (nmcli / networkd / ifupdown). + */ + get: operations['networking-get-interface']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + /** + * Apply interface configuration + * @description Replaces the interface's IPv4 configuration. The change is applied immediately but starts a rollback timer — if not confirmed within the timeout (default 60s), the prior configuration is automatically restored. This prevents lock-yourself-out mistakes on remote hosts. + */ + put: operations['networking-apply-config']; + trace?: never; + }; + '/api/networking/interfaces/{name}/confirm': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Confirm a pending change + * @description Cancels the rollback timer, making the applied configuration permanent. + */ + post: operations['networking-confirm-change']; + put?: never; + trace?: never; + }; + '/api/networking/interfaces/{name}/down': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Take an interface down + * @description Brings the interface down behind the rollback safety net: it is brought back up automatically if not confirmed within the timeout (default 60s). This prevents taking down the link you're managing the host over and losing access. + */ + post: operations['networking-link-down']; + put?: never; + trace?: never; + }; + '/api/networking/interfaces/{name}/rollback': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Immediately revert a pending change + * @description Reverts the interface to its prior configuration and clears the pending change. + */ + post: operations['networking-rollback-change']; + put?: never; + trace?: never; + }; + '/api/networking/interfaces/{name}/up': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** Bring an interface up */ + post: operations['networking-link-up']; + put?: never; + trace?: never; + }; + '/api/networking/pending': { + delete?: never; + /** + * Get pending change status + * @description Returns the currently pending change (interface name and seconds until auto-rollback), or 404 if there is no pending change. + */ + get: operations['networking-get-pending']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/networking/routes': { + delete?: never; + /** + * List the IPv4 route table + * @description Returns the kernel IPv4 route table via `ip -j route`. + */ + get: operations['networking-list-routes']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/packages': { + delete?: never; + /** List installed packages */ + get: operations['packages-list-installed']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Install a package (streamed) + * @description Installs a package, streaming the package manager's output as `output` events and ending with a `done` event carrying the exit status. + */ + post: operations['packages-install']; + put?: never; + trace?: never; + }; + '/api/packages/{name}': { + /** Remove a package (streamed) */ + delete: operations['packages-remove']; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/packages/updates': { + delete?: never; + /** List available updates */ + get: operations['packages-list-updates']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/packages/upgrade': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Upgrade all packages (streamed) + * @description Upgrades every package to its latest version, streaming the package manager's output live. + */ + post: operations['packages-upgrade']; + put?: never; + trace?: never; + }; + '/api/packages/upgrade/{name}': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Upgrade a single package (streamed) + * @description Upgrades the named package to its latest version, streaming the package manager's output live. apt uses `install --only-upgrade` so the package must already be installed; dnf/pacman handle this natively. + */ + post: operations['packages-upgrade-one']; + put?: never; + trace?: never; + }; + '/api/services': { + delete?: never; + /** + * List service units + * @description Returns all service units (active and inactive) via `systemctl list-units --type=service --all`. + */ + get: operations['services-list']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/services/{unit}': { + delete?: never; + /** + * Get a service's status + * @description Returns load/active/sub/unit-file state for one unit via `systemctl show`. Returns 404 when the unit does not exist. + */ + get: operations['services-get']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/services/{unit}/disable': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Disable a service at boot + * @description Disables the unit (`systemctl disable`). Returns 404 when the unit does not exist. + */ + post: operations['services-disable']; + put?: never; + trace?: never; + }; + '/api/services/{unit}/enable': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Enable a service at boot + * @description Enables the unit (`systemctl enable`). Returns 404 when the unit does not exist. + */ + post: operations['services-enable']; + put?: never; + trace?: never; + }; + '/api/services/{unit}/logs': { + delete?: never; + /** + * Get recent log records for a service + * @description Returns a snapshot of the unit's logs from the journal (default) or an allowlisted file (source=file&path=). Use /logs/stream to follow new records live. + */ + get: operations['services-logs']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/services/{unit}/logs/stream': { + delete?: never; + /** + * Stream a service's logs (Server-Sent Events) + * @description Follows the unit's journal (journalctl -f) or an allowlisted file (source=file&path=, via tail -F) and emits a `log` event per record. Stops when the client disconnects. + */ + get: operations['services-logs-stream']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/services/{unit}/restart': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Restart a service + * @description Restarts the unit (`systemctl restart`). Returns 404 when the unit does not exist. + */ + post: operations['services-restart']; + put?: never; + trace?: never; + }; + '/api/services/{unit}/start': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Start a service + * @description Starts the unit (`systemctl start`). Returns 404 when the unit does not exist. + */ + post: operations['services-start']; + put?: never; + trace?: never; + }; + '/api/services/{unit}/stop': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Stop a service + * @description Stops the unit (`systemctl stop`). Returns 404 when the unit does not exist. + */ + post: operations['services-stop']; + put?: never; + trace?: never; + }; + '/api/storage/fstab': { + delete?: never; + /** + * List /etc/fstab entries + * @description Returns the persistent mount definitions from /etc/fstab. + */ + get: operations['storage-list-fstab']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/storage/mounts': { + /** + * Unmount and remove a filesystem + * @description Unmounts the filesystem (if mounted) and removes its /etc/fstab entry. The mountpoint is passed as a query parameter since it contains slashes. + */ + delete: operations['storage-remove-mount']; + /** + * List active mounts + * @description Returns the kernel mount table (/proc/mounts). + */ + get: operations['storage-list-mounts']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Add and mount a filesystem + * @description Appends an /etc/fstab entry and mounts it. If the mount fails the fstab entry is rolled back, so a bad request leaves the system unchanged. + */ + post: operations['storage-add-mount']; + put?: never; + trace?: never; + }; + '/api/system/hostname': { + delete?: never; + /** + * Get system hostname + * @description Returns the current hostname as reported by hostnamectl. + */ + get: operations['system-get-hostname']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Set system hostname + * @description Sets the static hostname via hostnamectl, which owns /etc/hostname and manages the static/pretty/transient names. + */ + post: operations['system-set-hostname']; + put?: never; + trace?: never; + }; + '/api/system/info': { + delete?: never; + /** + * Get system information + * @description Returns an overview for a dashboard: OS/kernel identity, CPU, memory and swap, mounted disks, load averages, uptime, network interfaces, temperatures, and GPU information. All values come from cheap local reads (/proc, /sys, syscalls) with no D-Bus dependency; each section is best-effort. + */ + get: operations['system-get-info']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/system/keymap': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Set virtual console keymap + * @description Sets the virtual console keymap via localectl. The value is validated against the host's keymap list, so an unknown keymap returns 400. + */ + post: operations['system-set-keymap']; + put?: never; + trace?: never; + }; + '/api/system/keymaps': { + delete?: never; + /** + * List available console keymaps + * @description Returns every virtual console keymap known to the host. + */ + get: operations['system-list-keymaps']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/system/locale': { + delete?: never; + /** + * Get locale and keyboard layout + * @description Returns the system locale (LANG), virtual console keymap, and X11 layout from localectl. + */ + get: operations['system-get-locale']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Set system locale (LANG) + * @description Sets LANG via localectl. The value is validated against the host's locale list, so an unknown locale returns 400. + */ + post: operations['system-set-locale']; + put?: never; + trace?: never; + }; + '/api/system/locale/generate': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Generate (install) a new locale + * @description Generates a locale so it becomes available for use with set-locale. On Debian/Ubuntu/Arch this uncomments the entry in /etc/locale.gen and runs locale-gen; on RHEL/Fedora it uses localedef. Idempotent: if the locale is already generated, returns 200 immediately. + */ + post: operations['system-generate-locale']; + put?: never; + trace?: never; + }; + '/api/system/locales': { + delete?: never; + /** + * List available locales + * @description Returns every locale the host can be configured to use, suitable for populating a selector. + */ + get: operations['system-list-locales']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/system/ntp': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Enable or disable time synchronization + * @description Toggles network time synchronization via `timedatectl set-ntp`. Selecting specific NTP servers is not yet supported. Returns the resulting time status: on enable, `ntp` is true immediately, but `ntp_synchronized` stays false until the NTP daemon converges (seconds to minutes). + */ + post: operations['system-set-ntp']; + put?: never; + trace?: never; + }; + '/api/system/poweroff': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Power off the system + * @description Requires the `root` permission. The response is sent once `shutdown` accepts the request; for the immediate form it returns before the machine actually goes down, so a 200 does not guarantee a clean shutdown completed. + */ + post: operations['system-poweroff']; + put?: never; + trace?: never; + }; + '/api/system/reboot': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Reboot the system + * @description Requires the `root` permission. The response is sent once `shutdown` accepts the request; for the immediate form it returns before the machine actually goes down, so a 200 does not guarantee a clean shutdown completed. + */ + post: operations['system-reboot']; + put?: never; + trace?: never; + }; + '/api/system/time': { + delete?: never; + /** + * Get time, timezone and sync status + * @description Returns the current UTC time plus timezone and synchronization state from timedatectl. + */ + get: operations['system-get-time']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Set system time manually + * @description Sets the clock to an explicit RFC3339 time. This only works when NTP is disabled; otherwise timedatectl refuses and the call returns 409. + */ + post: operations['system-set-time']; + put?: never; + trace?: never; + }; + '/api/system/timezone': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Set system timezone + * @description Sets the timezone via timedatectl. The value is validated against the host's timezone list, so an unknown name returns 400. + */ + post: operations['system-set-timezone']; + put?: never; + trace?: never; + }; + '/api/system/timezones': { + delete?: never; + /** + * List available timezones + * @description Returns every IANA timezone name known to the host, suitable for populating a selector. + */ + get: operations['system-list-timezones']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/update': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Update nadir to the latest release + * @description Equivalent to running `sudo nadir update` on the host: queries server.release_repo for the latest release, downloads the binary matching the host's architecture, atomically replaces the running binary, and restarts the systemd unit. Returns 202 immediately; the service restart drops in-flight connections, so poll /api/health to confirm the new version is up. Requires the wildcard admin role. + */ + post: operations['meta-update']; + put?: never; + trace?: never; + }; + '/api/users': { + delete?: never; + /** + * List user accounts + * @description Returns every account in /etc/passwd, including system accounts (flagged via `system`). + */ + get: operations['users-list']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Create a user account + * @description Creates an account via useradd. The new account has a locked password until one is set via the password endpoint. 409 if the user already exists. + */ + post: operations['users-create']; + put?: never; + trace?: never; + }; + '/api/users/{username}': { + /** + * Delete a user account + * @description Removes an account via userdel. Pass ?remove_home=true to also delete the home directory. 404 if the user does not exist. + */ + delete: operations['users-delete']; + /** + * Get a single user + * @description Returns one account by login name. 404 if it does not exist. + */ + get: operations['users-get']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; + '/api/users/{username}/groups': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + /** + * Set a user's supplementary groups + * @description Replaces the user's full supplementary group set via `usermod -G` (an empty list removes them from all supplementary groups). Returns the resulting group membership. 404 if the user does not exist; 400 if any named group is missing. Requires the `root` permission: adding an account to wheel/sudo/docker is a privilege grant, not a routine write. + */ + put: operations['users-set-groups']; + trace?: never; + }; + '/api/users/{username}/password': { + delete?: never; + get?: never; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + /** + * Set a user's password + * @description Sets the password via chpasswd (fed over stdin, so the secret never appears in the process list). 404 if the user does not exist. Requires the `root` permission: resetting a privileged account's password (e.g. root) is a full-system action, not a routine write. + */ + post: operations['users-set-password']; + put?: never; + trace?: never; + }; + '/api/whoami': { + delete?: never; + /** + * Get the current user and their permissions + * @description Returns the authenticated username and, per module, the permissions the caller holds (wildcards resolved). Pair with /api/_modules to render the full permission matrix. + */ + get: operations['whoami']; + head?: never; + options?: never; + parameters: { + cookie?: never; + header?: never; + path?: never; + query?: never; + }; + patch?: never; + post?: never; + put?: never; + trace?: never; + }; } export type webhooks = Record; -export interface components { - schemas: { - ApplyOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ApplyOutputBody.json - */ - readonly $schema?: string; - /** - * @description Network backend that applied the change - * @example nmcli - */ - backend: string; - /** @example eth0 */ - interface: string; - /** - * Format: int64 - * @description Seconds until auto-rollback unless confirmed - * @example 60 - */ - rollback_seconds: number; - /** - * @description Always "pending" — confirm to make permanent - * @example pending - */ - status: string; - }; - AuditListOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/AuditListOutputBody.json - */ - readonly $schema?: string; - /** @description Recorded actions, newest first */ - entries: components["schemas"]["Entry"][] | null; - }; - CPUInfo: { - /** - * Format: int64 - * @description Peak current clock across all cores (instantaneous snapshot; 0 if cpufreq unavailable) - * @example 3157 - */ - current_mhz: number; - /** - * Format: int64 - * @description Number of logical CPUs (cores × threads) - * @example 16 - */ - logical_cpus: number; - /** - * Format: int64 - * @description Highest frequency (boost ceiling) - * @example 5137 - */ - max_mhz: number; - /** - * Format: int64 - * @description Lowest frequency the scaling governor can select - * @example 400 - */ - min_mhz: number; - /** - * @description CPU model name - * @example AMD Ryzen 7 7840U - */ - model: string; - }; - CoreUsage: { - /** - * Format: int64 - * @description Logical core index - * @example 0 - */ - core: number; - /** - * Format: double - * @description Usage percentage (0–100) - * @example 23.4 - */ - usage_pct: number; - }; - CreateGroupInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/CreateGroupInputBody.json - */ - readonly $schema?: string; - /** - * Format: int64 - * @description Explicit GID (omit to auto-assign) - * @example 1500 - */ - gid?: number; - /** - * @description Group name - * @example developers - */ - name: string; - /** @description Create a system group (groupadd --system) */ - system?: boolean; - }; - CreateUserInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/CreateUserInputBody.json - */ - readonly $schema?: string; - /** - * @description GECOS comment - * @example Alice Smith - */ - comment?: string; - /** @description Create the home directory (useradd -m) */ - create_home?: boolean; - /** - * @description Home directory (defaults to /home/) - * @example /home/alice - */ - home?: string; - /** - * @description Login shell - * @example /bin/bash - */ - shell?: string; - /** @description Create a system account (useradd --system) */ - system?: boolean; - /** - * @description Login name - * @example alice - */ - username: string; - }; - DNSOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/DNSOutputBody.json - */ - readonly $schema?: string; - /** - * @description Nameservers from /etc/resolv.conf - * @example [ - * "1.1.1.1" - * ] - */ - servers: string[] | null; - }; - DiskInfo: { - /** - * @description Backing device - * @example /dev/nvme0n1p2 - */ - filesystem: string; - /** - * Format: int64 - * @description Space available to unprivileged users - * @example 256000000000 - */ - free_bytes: number; - /** @example btrfs */ - fstype: string; - /** @example / */ - mountpoint: string; - /** - * Format: int64 - * @example 512000000000 - */ - total_bytes: number; - /** - * Format: int64 - * @example 256000000000 - */ - used_bytes: number; - }; - Entry: { - /** - * @description HTTP method - * @example POST - */ - method: string; - /** - * @description Target module - * @example users - */ - module: string; - /** - * @description Request path - * @example /api/users - */ - path: string; - /** - * Format: int64 - * @description HTTP response status - * @example 200 - */ - status: number; - /** - * @description When the action occurred (RFC3339, UTC) - * @example 2026-06-20T08:15:04Z - */ - time: string; - /** - * @description Who performed it - * @example alice - */ - username: string; - }; - ErrorDetail: { - /** @description Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id' */ - location?: string; - /** @description Error message text */ - message?: string; - /** @description The value at the given location */ - value?: unknown; - }; - ErrorEvent: { - message: string; - }; - ErrorModel: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ErrorModel.json - */ - readonly $schema?: string; - /** - * @description A human-readable explanation specific to this occurrence of the problem. - * @example Property foo is required but is missing. - */ - detail?: string; - /** @description Optional list of individual error details */ - errors?: components["schemas"]["ErrorDetail"][] | null; - /** - * Format: uri - * @description A URI reference that identifies the specific occurrence of the problem. - * @example https://example.com/error-log/abc123 - */ - instance?: string; - /** - * Format: int64 - * @description HTTP status code - * @example 400 - */ - status?: number; - /** - * @description A short, human-readable summary of the problem type. This value should not change between occurrences of the error. - * @example Bad Request - */ - title?: string; - /** - * Format: uri - * @description A URI reference to human-readable documentation for the error. - * @default about:blank - * @example https://example.com/errors/example - */ - type: string; - }; - FstabEntry: { - /** @example UUID=1234-5678 */ - device: string; - /** - * Format: int64 - * @example 0 - */ - dump: number; - /** @example ext4 */ - fstype: string; - /** @example /mnt/data */ - mountpoint: string; - /** @example defaults */ - options: string; - /** - * Format: int64 - * @description fsck order (0 = skip) - * @example 2 - */ - pass: number; - }; - GenerateLocaleInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/GenerateLocaleInputBody.json - */ - readonly $schema?: string; - /** - * @description Locale to generate (e.g. fr_FR.UTF-8) - * @example fr_FR.UTF-8 - */ - locale: string; - }; - Group: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/Group.json - */ - readonly $schema?: string; - /** - * Format: int64 - * @description Group ID - * @example 10 - */ - gid: number; - /** @description Supplementary members (primary-group members are not listed here) */ - members: string[] | null; - /** - * @description Group name - * @example wheel - */ - name: string; - /** @description True for system groups (gid < 1000) */ - system: boolean; - }; - HealthOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/HealthOutputBody.json - */ - readonly $schema?: string; - /** - * @description Embedded SQLite session store state - * @example ok - */ - database: string; - /** - * @description Overall health - * @example ok - */ - status: string; - /** - * @description Application version - * @example 1.0.0 - */ - version: string; - }; - HostEntry: { - /** - * @description Names mapped to the address - * @example [ - * "server", - * "server.local" - * ] - */ - hostnames: string[] | null; - /** - * @description IPv4 or IPv6 address - * @example 192.168.1.10 - */ - ip: string; - }; - HostUpsertInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/HostUpsertInputBody.json - */ - readonly $schema?: string; - /** - * @description Names to map to the IP - * @example [ - * "server", - * "server.local" - * ] - */ - hostnames: string[] | null; - }; - HostnameBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/HostnameBody.json - */ - readonly $schema?: string; - /** - * @description System hostname - * @example server01 - */ - hostname: string; - }; - IPv6Config: { - /** - * @description IPv6 address (static only) - * @example 2001:db8::10 - */ - address?: string; - /** - * @description IPv6 default gateway (static only, optional) - * @example 2001:db8::1 - */ - gateway?: string; - /** - * @description "auto" (SLAAC), "static", or "ignore" (disable IPv6) - * @example static - * @enum {string} - */ - method: "auto" | "static" | "ignore"; - /** - * Format: int64 - * @description Prefix length (static only) - * @example 64 - */ - prefix?: number; - }; - IfaceConfig: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/IfaceConfig.json - */ - readonly $schema?: string; - /** - * @description IPv4 address (static only) - * @example 192.168.1.10 - */ - address?: string; - /** - * @description DNS servers for this interface (IPv4 or IPv6) - * @example [ - * "1.1.1.1", - * "8.8.8.8" - * ] - */ - dns?: string[] | null; - /** - * @description Default gateway (static only, optional) - * @example 192.168.1.1 - */ - gateway?: string; - /** @description Optional IPv6 settings. Omit to leave IPv6 untouched; include to manage it. */ - ipv6?: components["schemas"]["IPv6Config"]; - /** - * @description "static" for a fixed address, "dhcp" for automatic - * @example static - * @enum {string} - */ - method: "static" | "dhcp"; - /** - * Format: int64 - * @description Network prefix length (static only) - * @example 24 - */ - prefix?: number; - /** - * Format: int64 - * @description Auto-revert after this many seconds unless confirmed. 0 uses the default (60s). - * @example 60 - */ - rollback_seconds?: number; - /** @description Static routes to install for this interface */ - routes?: components["schemas"]["Route"][] | null; - }; - InstallInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/InstallInputBody.json - */ - readonly $schema?: string; - /** - * @description Package to install - * @example htop - */ - name: string; - }; - Interface: { - /** - * @description IPv4 addresses in CIDR form - * @example [ - * "192.168.1.10/24" - * ] - */ - ipv4: string[] | null; - /** - * @description IPv6 addresses in CIDR form - * @example [ - * "fe80::1/64" - * ] - */ - ipv6: string[] | null; - /** @example 52:54:00:12:34:56 */ - mac: string; - /** - * Format: int64 - * @example 1500 - */ - mtu: number; - /** @example eth0 */ - name: string; - /** - * @description operstate: up / down / unknown - * @example up - */ - state: string; - }; - KeymapsOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/KeymapsOutputBody.json - */ - readonly $schema?: string; - /** @description Available virtual console keymaps */ - keymaps: string[] | null; - /** @description When keymaps is empty, why: e.g. "kbd not installed on this server" */ - reason?: string; - }; - ListFstabOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListFstabOutputBody.json - */ - readonly $schema?: string; - entries: components["schemas"]["FstabEntry"][] | null; - }; - ListGroupsOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListGroupsOutputBody.json - */ - readonly $schema?: string; - /** @description All groups from /etc/group */ - groups: components["schemas"]["Group"][] | null; - }; - ListHostsOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListHostsOutputBody.json - */ - readonly $schema?: string; - entries: components["schemas"]["HostEntry"][] | null; - }; - ListInterfacesOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListInterfacesOutputBody.json - */ - readonly $schema?: string; - interfaces: components["schemas"]["Interface"][] | null; - }; - ListMountsOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListMountsOutputBody.json - */ - readonly $schema?: string; - mounts: components["schemas"]["Mount"][] | null; - }; - ListOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListOutputBody.json - */ - readonly $schema?: string; - /** - * @description Detected package manager - * @example dnf - */ - manager: string; - packages: components["schemas"]["Package"][] | null; - }; - ListRoutesOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListRoutesOutputBody.json - */ - readonly $schema?: string; - routes: components["schemas"]["RouteEntry"][] | null; - }; - ListServicesOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListServicesOutputBody.json - */ - readonly $schema?: string; - /** @description All service units, active and inactive */ - services: components["schemas"]["ServiceUnit"][] | null; - }; - ListUsersOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ListUsersOutputBody.json - */ - readonly $schema?: string; - /** @description All accounts from /etc/passwd */ - users: components["schemas"]["User"][] | null; - }; - LoadInfo: { - /** @description Per-core CPU usage percentage (sampled over ~1 s); empty until the first sample completes */ - cpu_usage: components["schemas"]["CoreUsage"][] | null; - /** - * Format: double - * @example 0.42 - */ - load1: number; - /** - * Format: double - * @example 0.61 - */ - load15: number; - /** - * Format: double - * @example 0.55 - */ - load5: number; - }; - LocaleStatusBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/LocaleStatusBody.json - */ - readonly $schema?: string; - /** - * @description System locale (LANG) - * @example it_IT.UTF-8 - */ - lang: string; - /** - * @description Fallback language list (LANGUAGE) - * @example en_US: - */ - language: string; - /** - * @description Virtual console keymap - * @example it - */ - vc_keymap: string; - /** - * @description X11 keyboard layout - * @example it - */ - x11_layout: string; - }; - LocalesOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/LocalesOutputBody.json - */ - readonly $schema?: string; - /** @description Available locales */ - locales: string[] | null; - }; - LogEntry: { - /** @example Started OpenSSH server daemon. */ - message: string; - /** - * Format: int64 - * @description syslog priority 0 (emerg) – 7 (debug); 6 for file lines - * @example 6 - */ - priority: number; - /** - * @description Record timestamp (RFC3339, UTC); empty for file lines - * @example 2026-06-20T08:15:04Z - */ - time: string; - }; - LoginInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/LoginInputBody.json - */ - readonly $schema?: string; - /** @description System password */ - password: string; - /** @description System username */ - username: string; - }; - LoginOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/LoginOutputBody.json - */ - readonly $schema?: string; - /** @example logged in */ - status: string; - }; - LogoutOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/LogoutOutputBody.json - */ - readonly $schema?: string; - /** @example logged out */ - status: string; - }; - LogsOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/LogsOutputBody.json - */ - readonly $schema?: string; - /** @description Log records, oldest first */ - entries: components["schemas"]["LogEntry"][] | null; - }; - MemoryInfo: { - /** - * Format: int64 - * @description Memory available for new allocations without swapping - * @example 8192000000 - */ - available_bytes: number; - /** - * Format: int64 - * @example 8589934592 - */ - swap_free_bytes: number; - /** - * Format: int64 - * @example 8589934592 - */ - swap_total_bytes: number; - /** - * Format: int64 - * @example 16384000000 - */ - total_bytes: number; - /** - * Format: int64 - * @description total - available - * @example 8192000000 - */ - used_bytes: number; - }; - ModuleInfo: { - /** - * @description Stable module identifier - * @example system - */ - id: string; - /** - * @description Human-readable module name - * @example System - */ - name: string; - /** @description Permissions this module exposes (never includes the "*" wildcard) */ - permissions: string[] | null; - }; - ModulesOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ModulesOutputBody.json - */ - readonly $schema?: string; - /** @description Registered modules, sorted by ID */ - modules: components["schemas"]["ModuleInfo"][] | null; - }; - Mount: { - /** @example /dev/sda1 */ - device: string; - /** @example ext4 */ - fstype: string; - /** @example /mnt/data */ - mountpoint: string; - /** @example rw,relatime */ - options: string; - }; - MountInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/MountInputBody.json - */ - readonly $schema?: string; - /** - * @description Block device or UUID=/LABEL= specifier - * @example /dev/sdb1 - */ - device: string; - /** Format: int64 */ - dump?: number; - /** @example ext4 */ - fstype: string; - /** - * @description Absolute mount path - * @example /mnt/data - */ - mountpoint: string; - /** - * @description Mount options (default: defaults) - * @example defaults - */ - options?: string; - /** - * Format: int64 - * @description fsck order (default 2 for real filesystems, 0 to skip) - */ - pass?: number; - }; - NetInterface: { - /** @description Assigned addresses in CIDR notation */ - addresses: string[] | null; - /** @example aa:bb:cc:dd:ee:ff */ - mac: string; - /** @example eth0 */ - name: string; - /** @description Interface is administratively up */ - up: boolean; - }; - OSInfo: { - /** - * @description Machine hardware architecture (uname -m) - * @example x86_64 - */ - architecture: string; - /** - * @description System hostname - * @example server01 - */ - hostname: string; - /** - * @description Running kernel release (uname -r) - * @example 7.0.12-201.fc44.x86_64 - */ - kernel: string; - /** - * @description Distro name from /etc/os-release PRETTY_NAME - * @example Fedora Linux 44 (Workstation Edition) - */ - pretty_name: string; - }; - Package: { - /** - * @description Package name - * @example openssh-server - */ - name: string; - /** - * @description Installed version, or the available version for updates - * @example 9.6p1 - */ - version: string; - }; - PendingInfo: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/PendingInfo.json - */ - readonly $schema?: string; - /** - * @description Interface with a pending change - * @example eth0 - */ - interface: string; - /** - * Format: int64 - * @description Seconds until auto-rollback - * @example 45 - */ - seconds_remaining: number; - }; - PkgDoneEvent: { - /** @description Exit error when it failed */ - error?: string; - /** @description True if the package manager exited 0 */ - success: boolean; - }; - PkgErrorEvent: { - message: string; - }; - PkgOutputEvent: { - /** @description One line of the package manager's terminal output */ - line: string; - }; - PowerInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/PowerInputBody.json - */ - readonly $schema?: string; - /** - * @description When to act, as a shutdown(8) TIME (e.g. "+5" minutes, "23:00"). Empty or 'now' = immediate. - * @example +1 - */ - when?: string; - }; - Route: { - /** - * @description Destination network in CIDR notation, or "default" - * @example 10.0.0.0/24 - */ - destination: string; - /** - * @description Next-hop gateway - * @example 192.168.1.1 - */ - gateway: string; - }; - RouteEntry: { - /** - * @description Destination network, or "default" - * @example default - */ - destination: string; - /** @example 192.168.1.1 */ - gateway?: string; - /** @example eth0 */ - interface: string; - /** - * Format: int64 - * @example 100 - */ - metric?: number; - /** - * @description Preferred source address - * @example 192.168.1.10 - */ - source?: string; - }; - ServiceStatusBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/ServiceStatusBody.json - */ - readonly $schema?: string; - /** - * @description active / inactive / failed - * @example active - */ - active_state: string; - /** @example OpenSSH server daemon */ - description: string; - /** - * @description loaded / not-found / masked - * @example loaded - */ - load_state: string; - /** @example running */ - sub_state: string; - /** @example sshd.service */ - unit: string; - /** - * @description enabled / disabled / static - * @example enabled - */ - unit_file_state: string; - }; - ServiceUnit: { - /** - * @description High-level active state - * @example active - */ - active: string; - /** - * @description Unit description - * @example OpenSSH server daemon - */ - description: string; - /** - * @description Load state - * @example loaded - */ - load: string; - /** - * @description Low-level sub state - * @example running - */ - sub: string; - /** - * @description Unit name - * @example sshd.service - */ - unit: string; - }; - SetGroupsInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SetGroupsInputBody.json - */ - readonly $schema?: string; - /** @description Supplementary groups; replaces the user's full supplementary set */ - groups: string[] | null; - }; - SetKeymapInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SetKeymapInputBody.json - */ - readonly $schema?: string; - /** - * @description Virtual console keymap - * @example it - */ - keymap: string; - }; - SetLocaleInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SetLocaleInputBody.json - */ - readonly $schema?: string; - /** - * @description Locale to set as LANG - * @example it_IT.UTF-8 - */ - lang: string; - /** - * @description Fallback language list (LANGUAGE) - * @example en_US: - */ - language?: string; - }; - SetNTPInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SetNTPInputBody.json - */ - readonly $schema?: string; - /** @description Enable network time synchronization */ - enabled: boolean; - }; - SetPasswordInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SetPasswordInputBody.json - */ - readonly $schema?: string; - /** @description New password (sent to chpasswd over stdin, never argv) */ - password: string; - }; - SetTimeInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SetTimeInputBody.json - */ - readonly $schema?: string; - /** - * @description New time (RFC3339). Requires NTP disabled. - * @example 2026-06-19T13:36:31Z - */ - time: string; - }; - SetTimezoneInputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SetTimezoneInputBody.json - */ - readonly $schema?: string; - /** - * @description IANA timezone name - * @example Europe/Rome - */ - timezone: string; - }; - StatusOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/StatusOutputBody.json - */ - readonly $schema?: string; - /** - * @description Always "ok" on success - * @example ok - */ - status: string; - }; - SystemInfoBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/SystemInfoBody.json - */ - readonly $schema?: string; - /** - * @description Boot time (RFC3339, UTC) - * @example 2026-06-19T12:08:00Z - */ - boot_time: string; - /** @description Processor model and core count */ - cpu: components["schemas"]["CPUInfo"]; - /** @description Mounted block-device filesystems */ - disks: components["schemas"]["DiskInfo"][] | null; - /** @description Load averages (1/5/15 min) */ - load: components["schemas"]["LoadInfo"]; - /** @description RAM and swap usage in bytes */ - memory: components["schemas"]["MemoryInfo"]; - /** @description Network interfaces and their addresses */ - network_interfaces: components["schemas"]["NetInterface"][] | null; - /** @description OS and kernel identity */ - os: components["schemas"]["OSInfo"]; - /** @description Thermal sensor readings in Celsius */ - temperatures: components["schemas"]["Temperature"][] | null; - /** - * Format: int64 - * @description Seconds since boot - * @example 12490 - */ - uptime_seconds: number; - }; - Temperature: { - /** - * Format: double - * @example 47.5 - */ - celsius: number; - /** - * @description hwmon chip name; identifies the source (k10temp/coretemp=CPU, amdgpu/nvidia=GPU, nvme=disk) - * @example k10temp - */ - chip: string; - /** - * @description Per-sensor label, or the chip name when the sensor is unlabelled - * @example Tctl - */ - label: string; - }; - TimeStatusBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/TimeStatusBody.json - */ - readonly $schema?: string; - /** @description An NTP service is available on this host */ - can_ntp: boolean; - /** @description Hardware clock kept in local time instead of UTC */ - local_rtc: boolean; - /** @description Network time synchronization enabled */ - ntp: boolean; - /** @description Clock is currently synchronized */ - ntp_synchronized: boolean; - /** - * @description Current system time (RFC3339, UTC) - * @example 2026-06-19T13:36:31Z - */ - time: string; - /** - * @description IANA timezone name - * @example Europe/Rome - */ - timezone: string; - }; - TimezonesOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/TimezonesOutputBody.json - */ - readonly $schema?: string; - /** @description Available IANA timezone names */ - timezones: string[] | null; - }; - User: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/User.json - */ - readonly $schema?: string; - /** - * @description GECOS comment (often the full name) - * @example Alice Smith - */ - comment: string; - /** - * Format: int64 - * @description Primary group ID - * @example 1000 - */ - gid: number; - /** - * @description Home directory - * @example /home/alice - */ - home: string; - /** - * @description Login shell - * @example /bin/bash - */ - shell: string; - /** @description True for system accounts (uid < 1000) */ - system: boolean; - /** - * Format: int64 - * @description User ID - * @example 1000 - */ - uid: number; - /** - * @description Login name - * @example alice - */ - username: string; - }; - UserGroupsOutputBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/UserGroupsOutputBody.json - */ - readonly $schema?: string; - /** @description All groups the user belongs to (primary + supplementary) */ - groups: string[] | null; - /** @example alice */ - username: string; - }; - WhoamiBody: { - /** - * Format: uri - * @description A URL to the JSON Schema for this object. - * @example https://example.com/schemas/WhoamiBody.json - */ - readonly $schema?: string; - /** @description Module ID -> permissions the caller holds. Modules where they hold none are omitted. */ - permissions: { - [key: string]: string[] | null; - }; - /** - * @description Authenticated username - * @example urania - */ - username: string; - }; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type $defs = Record; -export interface operations { - "list-modules": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ModulesOutputBody"]; - }; - }; - /** @description Error */ - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "audit-list": { - parameters: { - query?: { - /** @description Max entries to return, newest first */ - limit?: number; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["AuditListOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "groups-list": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListGroupsOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "groups-create": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateGroupInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Group"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "groups-get": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Group name */ - group: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Group"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "groups-delete": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Group name */ - group: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - health: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HealthOutputBody"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Service Unavailable */ - 503: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - login: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["LoginInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - "Set-Cookie"?: string; - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LoginOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Too Many Requests */ - 429: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - logout: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: { - nadir_session_id?: string; - }; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - "Set-Cookie"?: string; - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LogoutOutputBody"]; - }; - }; - /** @description Error */ - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-get-dns": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["DNSOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-list-hosts": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListHostsOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-upsert-host": { - parameters: { - query?: never; - header?: never; - path: { - /** @description IP address to add or update */ - ip: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["HostUpsertInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-delete-host": { - parameters: { - query?: never; - header?: never; - path: { - /** @description IP address whose entry to remove */ - ip: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-list-interfaces": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListInterfacesOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-get-interface": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Interface name */ - name: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["IfaceConfig"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-apply-config": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Interface name */ - name: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["IfaceConfig"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ApplyOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-confirm-change": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Interface name */ - name: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-link-down": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Interface name */ - name: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ApplyOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-rollback-change": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Interface name */ - name: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-link-up": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Interface name */ - name: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-get-pending": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PendingInfo"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "networking-list-routes": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListRoutesOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "packages-list-installed": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "packages-install": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["InstallInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/event-stream": ({ - data: components["schemas"]["PkgDoneEvent"]; - /** - * @description The event name. - * @constant - */ - event: "done"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgErrorEvent"]; - /** - * @description The event name. - * @constant - */ - event: "error"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgOutputEvent"]; - /** - * @description The event name. - * @constant - */ - event: "output"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - })[]; - }; - }; - /** @description Error */ - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "packages-list-updates": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "packages-upgrade": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/event-stream": ({ - data: components["schemas"]["PkgDoneEvent"]; - /** - * @description The event name. - * @constant - */ - event: "done"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgErrorEvent"]; - /** - * @description The event name. - * @constant - */ - event: "error"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgOutputEvent"]; - /** - * @description The event name. - * @constant - */ - event: "output"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - })[]; - }; - }; - /** @description Error */ - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "packages-upgrade-one": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Package to upgrade */ - name: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/event-stream": ({ - data: components["schemas"]["PkgDoneEvent"]; - /** - * @description The event name. - * @constant - */ - event: "done"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgErrorEvent"]; - /** - * @description The event name. - * @constant - */ - event: "error"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgOutputEvent"]; - /** - * @description The event name. - * @constant - */ - event: "output"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - })[]; - }; - }; - /** @description Error */ - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "packages-remove": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Package to remove */ - name: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/event-stream": ({ - data: components["schemas"]["PkgDoneEvent"]; - /** - * @description The event name. - * @constant - */ - event: "done"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgErrorEvent"]; - /** - * @description The event name. - * @constant - */ - event: "error"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["PkgOutputEvent"]; - /** - * @description The event name. - * @constant - */ - event: "output"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - })[]; - }; - }; - /** @description Error */ - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-list": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListServicesOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-get": { - parameters: { - query?: never; - header?: never; - path: { - /** @description systemd unit name */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceStatusBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-disable": { - parameters: { - query?: never; - header?: never; - path: { - /** @description systemd unit name */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-enable": { - parameters: { - query?: never; - header?: never; - path: { - /** @description systemd unit name */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-logs": { - parameters: { - query?: { - /** @description Where to read logs from */ - source?: "journal" | "file"; - /** @description Log file (file source only); must be allowlisted for this unit in config */ - path?: string; - /** @description How many recent records to return (max 10000) */ - lines?: number; - /** @description journalctl time filter (journal source only) */ - since?: string; - /** @description Max syslog priority to include: 0 emerg .. 7 debug (journal source only). 7 = all. */ - priority?: number; - }; - header?: never; - path: { - /** @description Unit name as listed by GET /api/services; the trailing .service is optional */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LogsOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-logs-stream": { - parameters: { - query?: { - /** @description Where to stream logs from */ - source?: "journal" | "file"; - /** @description Log file (file source only); must be allowlisted for this unit in config */ - path?: string; - /** @description Backfill window (journal source only) */ - since?: string; - /** @description Max syslog priority to include: 0 emerg .. 7 debug (journal source only). 7 = all. */ - priority?: number; - }; - header?: never; - path: { - /** @description Unit name as listed by GET /api/services; the trailing .service is optional */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/event-stream": ({ - data: components["schemas"]["ErrorEvent"]; - /** - * @description The event name. - * @constant - */ - event: "error"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - } | { - data: components["schemas"]["LogEntry"]; - /** - * @description The event name. - * @constant - */ - event: "log"; - /** @description The event ID. */ - id?: number; - /** @description The retry time in milliseconds. */ - retry?: number; - })[]; - }; - }; - /** @description Error */ - default: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-restart": { - parameters: { - query?: never; - header?: never; - path: { - /** @description systemd unit name */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-start": { - parameters: { - query?: never; - header?: never; - path: { - /** @description systemd unit name */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "services-stop": { - parameters: { - query?: never; - header?: never; - path: { - /** @description systemd unit name */ - unit: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "storage-list-fstab": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListFstabOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "storage-list-mounts": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListMountsOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "storage-add-mount": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["MountInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "storage-remove-mount": { - parameters: { - query?: { - /** @description Mountpoint to unmount and remove from fstab */ - mountpoint?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-get-hostname": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HostnameBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-set-hostname": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["HostnameBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-get-info": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SystemInfoBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-set-keymap": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SetKeymapInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-list-keymaps": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["KeymapsOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-get-locale": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LocaleStatusBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-set-locale": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SetLocaleInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-generate-locale": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["GenerateLocaleInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-list-locales": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LocalesOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-set-ntp": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SetNTPInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeStatusBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-poweroff": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["PowerInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-reboot": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["PowerInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-get-time": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeStatusBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-set-time": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SetTimeInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-set-timezone": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SetTimezoneInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "system-list-timezones": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimezonesOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "terminal-connect": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description No Content */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Upgrade Required */ - 426: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "meta-update": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Accepted */ - 202: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "users-list": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListUsersOutputBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "users-create": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateUserInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["User"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "users-get": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Login name */ - username: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["User"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "users-delete": { - parameters: { - query?: { - /** @description Also remove the home directory and mail spool (userdel -r) */ - remove_home?: boolean; - }; - header?: never; - path: { - /** @description Login name */ - username: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "users-set-groups": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Login name */ - username: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SetGroupsInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["UserGroupsOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Conflict */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - "users-set-password": { - parameters: { - query?: never; - header?: never; - path: { - /** @description Login name */ - username: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SetPasswordInputBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["StatusOutputBody"]; - }; - }; - /** @description Bad Request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Not Found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; - whoami: { - parameters: { - query?: never; - header?: { - Authorization?: string; - }; - path?: never; - cookie?: { - nadir_session_id?: string; - }; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["WhoamiBody"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Unprocessable Entity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Too Many Requests */ - 429: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - /** @description Internal Server Error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/problem+json": components["schemas"]["ErrorModel"]; - }; - }; - }; - }; -} diff --git a/src/lib/server/terminal/session.ts b/src/lib/server/terminal/session.ts index cc806a1..9450a18 100644 --- a/src/lib/server/terminal/session.ts +++ b/src/lib/server/terminal/session.ts @@ -167,15 +167,27 @@ export function writeToSession(sessionId: string, data: string): void { function safeClose(ctrl: null | ReadableStreamDefaultController) { if (!ctrl) return; - try { ctrl.close(); } catch { /* controller already closed */ } + try { + ctrl.close(); + } catch { + /* controller already closed */ + } } function safeEnqueue(ctrl: null | ReadableStreamDefaultController, data: string) { if (!ctrl) return; - try { ctrl.enqueue(data); } catch { /* controller already closed */ } + try { + ctrl.enqueue(data); + } catch { + /* controller already closed */ + } } function safeError(ctrl: null | ReadableStreamDefaultController, err: Error) { if (!ctrl) return; - try { ctrl.error(err); } catch { /* controller already errored */ } + try { + ctrl.error(err); + } catch { + /* controller already errored */ + } } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index ca93cd4..d58eb43 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -14,9 +14,20 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } - export function extractErrorMessage(err: unknown): string | undefined { if (!err || typeof err !== 'object') return undefined; const a = err as AgentError; return a.body?.message ?? a.message ?? a.detail ?? undefined; -} \ No newline at end of file +} + +export function hasPermission( + moduleId: string, + perm: 'read' | 'root' | 'write', + userPerms: null | Record | undefined +): boolean { + if (!userPerms) return false; + if (userPerms['*']) return true; + const modPerms = userPerms[moduleId]; + if (!modPerms) return false; + return modPerms.includes('*') || modPerms.includes(perm); +} diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index b07fba3..b74dffb 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -40,7 +40,7 @@ -
+
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9de691c..070122a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -16,9 +16,8 @@ let { children } = $props(); const user = $derived(getUser()); - const showSidebar = - (cU: null | User) => - cU && (page.url.pathname.startsWith('/dashboard') || page.url.pathname.startsWith('/admin')) + const showSidebar = (cU: null | User) => + cU && (page.url.pathname.startsWith('/dashboard') || page.url.pathname.startsWith('/admin')); class TerminalState { open = $state(false); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 6c3a97d..c597530 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,8 +1,460 @@ - -

Welcome to SvelteKit

-

Visit svelte.dev/docs/kit to read the documentation

+ + + +
+ + + {m.appname()} + + +
+ +
+ +
+
+
+
+ + {m.landing_tech_opensource()} • {m.landing_tech_selfhosted()} + +

+ {m.landing_hero_title()} +

+

+ {m.landing_hero_tagline()} +

+ +
+ + +
+
+ + + + nadir-webui — dashboard +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
// Querying nadir-agent metrics...
+
$ curl -fsSL https://{nadirHost}/api/health
+
+ { "status": "healthy", "version": "v1.2.0", "uptime": "142d 18h" } +
+
+
+
+
+
+ + +
+
+
+

+ {m.landing_arch_title()} +

+

+ {m.landing_arch_desc()} +

+
+ +
+ + + + Frontend + {m.landing_arch_frontend_title()} + + {m.landing_arch_frontend_desc()} + + + +
+ {dockerCommand} + +
+
+
+ + + + + Backend + {m.landing_arch_backend_title()} + + {m.landing_arch_backend_desc()} + + + +
+ {installCommand} + +
+
+
+
+ + +
+
+ + {m.landing_security_architecture_title()} +
+

{m.landing_security_architecture_desc()}

+
+
+
+ + +
+
+
+

+ {m.landing_features_title()} +

+

+ {m.landing_features_desc()} +

+
+ +
+ {#each features as feature (feature.title)} +
+
+ +

{feature.title}

+
+

{feature.desc}

+
+ {/each} +
+
+
+ + +
+
+
+

+ {m.landing_how_title()} +

+

+ {m.landing_how_desc()} +

+
+ +
+ +
+
+ 1 +
+
+
+

{m.landing_how_step1_title()}

+

+ {m.landing_how_step1_desc()} +

+
+
+ {dockerCommand} + +
+
+
+ + +
+
+ 2 +
+
+
+

{m.landing_how_step2_title()}

+

+ {m.landing_how_step2_desc()} +

+
+
+ {installCommand} + +
+
+ +
+ Security note: + {m.landing_security_note().replace('Security note:', '').trim()} +
+
+
+
+ + +
+
+ 3 +
+
+

{m.landing_how_step3_title()}

+

+ {m.landing_how_step3_desc()} +

+
+
+
+
+
+
+ + +
+
+ + + {m.appname()} + +

+ {m.landing_footer_subtitle()} +

+ +
+ + +
+
+
diff --git a/src/routes/auth/forgot-password/+page.svelte b/src/routes/auth/forgot-password/+page.svelte index 8b74a98..20d306d 100644 --- a/src/routes/auth/forgot-password/+page.svelte +++ b/src/routes/auth/forgot-password/+page.svelte @@ -28,7 +28,9 @@ {#if requestReset.result?.sent} - + {:else}
requestReset.validate()} @@ -37,9 +39,7 @@ await submit(); } catch (error) { console.error(error); - toast.error( - extractErrorMessage(error) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(error) ?? m.errors_generic()); } })} > diff --git a/src/routes/auth/reset-password/+page.svelte b/src/routes/auth/reset-password/+page.svelte index fe1c1a4..2c9eba6 100644 --- a/src/routes/auth/reset-password/+page.svelte +++ b/src/routes/auth/reset-password/+page.svelte @@ -36,9 +36,7 @@ await submit(); } catch (error) { console.error(error); - toast.error( - extractErrorMessage(error) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(error) ?? m.errors_generic()); } })} > diff --git a/src/routes/auth/sign-in/+page.svelte b/src/routes/auth/sign-in/+page.svelte index 5d6a54e..f551a40 100644 --- a/src/routes/auth/sign-in/+page.svelte +++ b/src/routes/auth/sign-in/+page.svelte @@ -11,8 +11,8 @@ import { Input } from '$lib/components/ui/input/index.js'; import { m } from '$lib/paraglide/messages'; import { getOAuthProviders, login } from '$lib/remotes/auth.remote'; - import { cn } from '$lib/utils.js'; import { extractErrorMessage } from '$lib/utils'; + import { cn } from '$lib/utils.js'; import { toast } from 'svelte-sonner'; const id = $props.id(); @@ -36,9 +36,7 @@ await submit(); } catch (error) { console.error(error); - toast.error( - extractErrorMessage(error) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(error) ?? m.errors_generic()); } })} > diff --git a/src/routes/auth/sign-up/+page.svelte b/src/routes/auth/sign-up/+page.svelte index 653337c..5b085bb 100644 --- a/src/routes/auth/sign-up/+page.svelte +++ b/src/routes/auth/sign-up/+page.svelte @@ -24,7 +24,9 @@ > - + {:else} @@ -41,9 +43,7 @@ await submit(); } catch (error) { console.error(error); - toast.error( - extractErrorMessage(error) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(error) ?? m.errors_generic()); } })} > diff --git a/src/routes/dashboard/+page.svelte b/src/routes/dashboard/+page.svelte index 0e9078c..beddf85 100644 --- a/src/routes/dashboard/+page.svelte +++ b/src/routes/dashboard/+page.svelte @@ -1,6 +1,366 @@ + machines.refresh()} + bind:page + i18n={{ + display: m.dashboard_filter_display(), + filterTitle: m.dashboard_filter_title(), + next: () => m.dashboard_next(), + pageOf: (p) => m.dashboard_page_of(p), + previous: () => m.dashboard_prev(), + rowsPerPage: m.dashboard_rows_per_page() + }} +> + {#snippet actions()} + + {/snippet} + {#snippet columns()} + {#each [{ key: 'name', label: m.machine_name() }, { key: 'address', label: m.machine_address() }] as col (col.key)} + + + + {/each} + {m.dashboard_col_status()} + {m.dashboard_col_actions()} + {/snippet} + {#snippet row(machine: Machine)} + {@const health = machineHealth(machine.id).current} + + {machine.name ?? machine.id.slice(0, 8)} + + {machine.address} + + + + {health ? m.machine_online() : m.machine_offline()} + + + + + + {#snippet child({ props })} + + {/snippet} + + + { + editingMachine = machine; + editOpen = true; + }}>{m.machine_edit()} + { + deletingMachine = machine; + deleteOpen = true; + }}>{m.delete()} + + + + {/snippet} + + + + + + {m.machine_add()} + {m.machine_add_description()} + + addMachine.validate()} + {...addMachine.preflight(machineSchema).enhance(async ({ submit }) => { + try { + await submit(); + toast.success(m.machine_add()); + addOpen = false; + await afterMutate(); + } catch (e) { + handleError(e); + } + })} + > + + + {m.machine_name()} + + {#each addMachine.fields.name.issues() as issue, i (`${issue}-${i}`)} + {issue.message} + {/each} + + + {m.machine_address()} + + {#each addMachine.fields.address.issues() as issue, i (`${issue}-${i}`)} + {issue.message} + {/each} + + + {m.machine_token()} + + {#each addMachine.fields.token.issues() as issue, i (`${issue}-${i}`)} + {issue.message} + {/each} + + + + + + + +{#if editingMachine} + {@const editForm = updateMachine.for(editingMachine.id)} + + + + {m.machine_edit()} + {m.machine_edit_description()} + +
editForm.validate()} + {...editForm.preflight(machineEditSchema).enhance(async ({ submit }) => { + try { + await submit(); + toast.success(m.machine_edit()); + editOpen = false; + editingMachine = null; + await afterMutate(); + } catch (e) { + handleError(e); + } + })} + > + + + + {m.machine_name()} + + {#each editForm.fields.name.issues() as issue, i (`${issue}-${i}`)} + {issue.message} + {/each} + + + {m.machine_address()} + + {#each editForm.fields.address.issues() as issue, i (`${issue}-${i}`)} + {issue.message} + {/each} + + + {m.machine_token()} + + {m.machine_token_keep()} + {#each editForm.fields.token.issues() as issue, i (`${issue}-${i}`)} + {issue.message} + {/each} + + + +
+
+
+{/if} + + + + + {m.machine_delete_title()} + + {m.machine_delete_confirm({ name: deletingMachine?.name ?? '' })} + + +
{ + try { + await submit(); + toast.success(m.machine_delete_title()); + deleteOpen = false; + deletingMachine = null; + await afterMutate(); + } catch (e) { + handleError(e); + } + })} + > + + + {m.cancel()} + + {m.delete()} + + +
+
+
diff --git a/src/routes/dashboard/[machineId]/+page.svelte b/src/routes/dashboard/[machineId]/+page.svelte index d4f8df5..d0eb507 100644 --- a/src/routes/dashboard/[machineId]/+page.svelte +++ b/src/routes/dashboard/[machineId]/+page.svelte @@ -6,9 +6,7 @@ import Ellipsis from '@lucide/svelte/icons/ellipsis'; import Globe from '@lucide/svelte/icons/globe'; import MemoryStick from '@lucide/svelte/icons/memory-stick'; - import Pause from '@lucide/svelte/icons/pause'; import Pencil from '@lucide/svelte/icons/pencil'; - import Play from '@lucide/svelte/icons/play'; import RefreshCw from '@lucide/svelte/icons/refresh-cw'; import Server from '@lucide/svelte/icons/server'; import Trash2 from '@lucide/svelte/icons/trash-2'; @@ -23,11 +21,13 @@ import { fmtDateTime, gb, + ghz, pct, uptime, usageBar, usageText } from '$lib/components/dashboard/format'; + import GpuCard from '$lib/components/dashboard/gpu-card.svelte'; import KpiCard from '$lib/components/dashboard/kpi-card.svelte'; import NetworkPanel from '$lib/components/dashboard/network-panel.svelte'; import StoragePanel from '$lib/components/dashboard/storage-panel.svelte'; @@ -36,13 +36,14 @@ import PageMeta from '$lib/components/seo/page-meta.svelte'; import * as AlertDialog from '$lib/components/ui/alert-dialog'; import { Button } from '$lib/components/ui/button'; - import { ButtonGroup } from '$lib/components/ui/button-group'; import * as Card from '$lib/components/ui/card'; + import { Checkbox } from '$lib/components/ui/checkbox'; import * as Dialog from '$lib/components/ui/dialog'; import * as DropdownMenu from '$lib/components/ui/dropdown-menu'; import * as Empty from '$lib/components/ui/empty'; import * as Field from '$lib/components/ui/field'; import { Input } from '$lib/components/ui/input'; + import * as Popover from '$lib/components/ui/popover'; import { Progress } from '$lib/components/ui/progress'; import * as Select from '$lib/components/ui/select'; import { Spinner } from '$lib/components/ui/spinner'; @@ -230,43 +231,47 @@

- - - {intervalLabel} - - {#each intervals as opt (opt.value)} - {opt.label} - {/each} - - - {#if syncInterval === 'custom'} -
- + + + {#snippet child({ props })} + + {/snippet} + + +
+ + {#if polling} + + {intervalLabel} + + {#each intervals as opt (opt.value)} + {opt.label} + {/each} + + + {#if syncInterval === 'custom'} + + {/if} + {/if}
- {/if} +
+
+ {#if !polling} - - + {/if} {#snippet child({ props })} @@ -306,8 +311,6 @@ try { await updateAgent(machineId); toast.info(m.agent_update_started({ from, to })); - // Agent returns 202 and runs update in background; poll until the - // reported version flips or we give up. const deadline = Date.now() + 60_000; let done = false; while (Date.now() < deadline) { @@ -324,9 +327,7 @@ } if (!done) toast.error(m.agent_update_failed()); } catch (e) { - toast.error( - extractErrorMessage(e) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(e) ?? m.errors_generic()); } finally { updating = false; } @@ -338,14 +339,20 @@
-
- +
+ value={ghz(sys.cpu.current_mhz)} + valueClass="tabular-nums {usageText(pct(sys.cpu.current_mhz, sys.cpu.max_mhz))}" + detail={sys.cpu.model} + > + {#snippet extra()} +
+ {m.dashboard_logical_cores({ cores: sys.cpu.logical_cpus })} +
+ {/snippet} +
-
-
+
+
+ + {#if sys.gpus?.length} + + {/if} + +
+
+ + +
- - -
- -
- - - -
- -
-
{/if} @@ -442,9 +447,7 @@ toast.success(m.machine_edit()); } catch (error) { console.error(error); - toast.error( - extractErrorMessage(error) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(error) ?? m.errors_generic()); } })} > @@ -513,9 +516,7 @@ goto(resolve('/dashboard')); } catch (error) { console.error(error); - toast.error( - extractErrorMessage(error) ?? m.errors_generic() - ); + toast.error(extractErrorMessage(error) ?? m.errors_generic()); } })} > @@ -538,21 +539,11 @@ background-image: linear-gradient(to right, currentColor 50%, transparent 0%); background-size: 10px 2px; background-repeat: repeat-x; - animation: custom-flow 1s linear infinite; } .custom-dash-ok { color: var(--color-emerald-500, #10b981); } .custom-dash-bad { color: var(--destructive, #ef4444); - animation-duration: 2s; - } - @keyframes custom-flow { - from { - background-position: 0 0; - } - to { - background-position: 10px 0; - } } diff --git a/src/routes/dashboard/[machineId]/networking/+page.svelte b/src/routes/dashboard/[machineId]/networking/+page.svelte index 9ba3ea1..7215e4a 100644 --- a/src/routes/dashboard/[machineId]/networking/+page.svelte +++ b/src/routes/dashboard/[machineId]/networking/+page.svelte @@ -13,7 +13,7 @@ -
+

{m.nav_networking()}

diff --git a/src/routes/dashboard/[machineId]/networking/dns/+page.svelte b/src/routes/dashboard/[machineId]/networking/dns/+page.svelte index b0221a1..0bd9d64 100644 --- a/src/routes/dashboard/[machineId]/networking/dns/+page.svelte +++ b/src/routes/dashboard/[machineId]/networking/dns/+page.svelte @@ -12,7 +12,7 @@ -
+

{m.nav_networking_dns()}

diff --git a/src/routes/dashboard/[machineId]/networking/hosts/+page.svelte b/src/routes/dashboard/[machineId]/networking/hosts/+page.svelte index ba0eb47..00d4be0 100644 --- a/src/routes/dashboard/[machineId]/networking/hosts/+page.svelte +++ b/src/routes/dashboard/[machineId]/networking/hosts/+page.svelte @@ -17,7 +17,8 @@ import * as Table from '$lib/components/ui/table'; import { m } from '$lib/paraglide/messages'; import { deleteHost, listHosts, upsertHost } from '$lib/remotes/networking.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; type Host = { hostnames: null | string[]; ip: string }; @@ -25,6 +26,8 @@ const id = $props.id(); const machineId = $derived(pageState.params.machineId!); const hosts = $derived(listHosts(machineId)); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('networking', 'write', whoami.current?.permissions)); let search = $state(''); let editOpen = $state(false); @@ -136,16 +139,16 @@ bind:search onrefresh={() => hosts.refresh()} i18n={{ + display: m.users_filter_display(), + filterTitle: m.users_filter(), + next: () => m.pagination_next(), pageOf: (p) => m.pagination_page_of({ page: p.page, pages: p.pages }), previous: () => m.pagination_previous(), - next: () => m.pagination_next(), - filterTitle: m.users_filter(), - display: m.users_filter_display(), rowsPerPage: m.users_rows_per_page() }} > {#snippet actions()} - @@ -172,9 +175,12 @@ {/snippet} - openEdit(h)}>{m.edit()} + openEdit(h)} disabled={!canWrite} + >{m.edit()} { deleting = h; deleteOpen = true; diff --git a/src/routes/dashboard/[machineId]/networking/interfaces/+page.svelte b/src/routes/dashboard/[machineId]/networking/interfaces/+page.svelte index 997dd1d..ea76d2d 100644 --- a/src/routes/dashboard/[machineId]/networking/interfaces/+page.svelte +++ b/src/routes/dashboard/[machineId]/networking/interfaces/+page.svelte @@ -23,12 +23,15 @@ listInterfaces, rollbackChange } from '$lib/remotes/networking.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; const machineId = $derived(pageState.params.machineId!); const interfaces = $derived(listInterfaces(machineId)); const pending = $derived(getPending(machineId)); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('networking', 'write', whoami.current?.permissions)); let search = $state(''); let busy = $state(null); @@ -86,49 +89,47 @@ title={m.seo_title_networking_interfaces()} description={m.seo_desc_networking_interfaces()} /> -
- {#if pending.current} -
- - {m.networking_pending_banner({ - iface: pending.current.interface, - seconds: pending.current.seconds_remaining - })} - -
- - -
+{#if pending.current} +
+ + {m.networking_pending_banner({ + iface: pending.current.interface, + seconds: pending.current.seconds_remaining + })} + +
+ +
- {/if} -
+
+{/if} {#snippet sortHead(key: string, label: string)}
- @@ -270,6 +273,7 @@ size="icon" class="size-9 shrink-0" onclick={() => removeDns(i)} + disabled={!canWrite} > @@ -285,7 +289,7 @@ {m.networking_routes_section()} {m.networking_routes_section_hint()}
- @@ -308,6 +312,7 @@ size="icon" class="size-9 shrink-0" onclick={() => removeRoute(i)} + disabled={!canWrite} > @@ -331,7 +336,9 @@ />

{m.networking_rollback_seconds_hint()}

- + @@ -348,7 +355,7 @@ {m.cancel()} - + {m.networking_apply()} diff --git a/src/routes/dashboard/[machineId]/networking/routes/+page.svelte b/src/routes/dashboard/[machineId]/networking/routes/+page.svelte index 6cff5e2..4233ae1 100644 --- a/src/routes/dashboard/[machineId]/networking/routes/+page.svelte +++ b/src/routes/dashboard/[machineId]/networking/routes/+page.svelte @@ -78,11 +78,11 @@ bind:search onrefresh={() => routes.refresh()} i18n={{ + display: m.users_filter_display(), + filterTitle: m.users_filter(), + next: () => m.pagination_next(), pageOf: (p) => m.pagination_page_of({ page: p.page, pages: p.pages }), previous: () => m.pagination_previous(), - next: () => m.pagination_next(), - filterTitle: m.users_filter(), - display: m.users_filter_display(), rowsPerPage: m.users_rows_per_page() }} > diff --git a/src/routes/dashboard/[machineId]/packages/+page.svelte b/src/routes/dashboard/[machineId]/packages/+page.svelte index 589f656..d7e6492 100644 --- a/src/routes/dashboard/[machineId]/packages/+page.svelte +++ b/src/routes/dashboard/[machineId]/packages/+page.svelte @@ -11,7 +11,7 @@ -
+

{m.nav_packages()}

diff --git a/src/routes/dashboard/[machineId]/packages/installed/+page.svelte b/src/routes/dashboard/[machineId]/packages/installed/+page.svelte index 2736af5..e88f353 100644 --- a/src/routes/dashboard/[machineId]/packages/installed/+page.svelte +++ b/src/routes/dashboard/[machineId]/packages/installed/+page.svelte @@ -25,6 +25,8 @@ listPackageUpdates, streamPackageAction } from '$lib/remotes/packages.remote'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { hasPermission } from '$lib/utils'; type Package = { name: string; version: string }; @@ -34,6 +36,8 @@ const updatesResource = $derived(listPackageUpdates(machineId)); const manager = $derived(dataResource.current?.manager ?? ''); const packages = $derived((dataResource.current?.packages ?? []) as Package[]); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('packages', 'write', whoami.current?.permissions)); // name → new version, for the per-row "update available" badge. const updatesMap = $derived( new Map( @@ -117,13 +121,19 @@ ]; consoleStatus = 'success'; } else { - consoleLogs = [...consoleLogs, `\n${chunk.data.error ? m.packages_stream_error({ message: chunk.data.error }) : m.packages_stream_failed()}`]; + consoleLogs = [ + ...consoleLogs, + `\n${chunk.data.error ? m.packages_stream_error({ message: chunk.data.error }) : m.packages_stream_failed()}` + ]; consoleStatus = 'failed'; } } } } catch (err) { - consoleLogs = [...consoleLogs, `\n${m.packages_stream_connection_error({ message: (err as Error).message || `${err}` })}`]; + consoleLogs = [ + ...consoleLogs, + `\n${m.packages_stream_connection_error({ message: (err as Error).message || `${err}` })}` + ]; consoleStatus = 'failed'; } finally { await dataResource.refresh(); @@ -173,7 +183,7 @@ }} > {#snippet actions()} - @@ -199,7 +209,7 @@ {/snippet} {#snippet row(p: Package)} {@const newVersion = updatesMap.get(p.name)} - + {p.name} {p.version} @@ -225,7 +235,7 @@ {#if newVersion} - runUpdate(p.name)}> + runUpdate(p.name)} disabled={!canWrite}> {m.packages_update_single()} @@ -233,6 +243,7 @@ {/if} { deleting = p; deleteOpen = true; diff --git a/src/routes/dashboard/[machineId]/packages/updates/+page.svelte b/src/routes/dashboard/[machineId]/packages/updates/+page.svelte index a98513f..951b2f8 100644 --- a/src/routes/dashboard/[machineId]/packages/updates/+page.svelte +++ b/src/routes/dashboard/[machineId]/packages/updates/+page.svelte @@ -15,6 +15,8 @@ import * as Table from '$lib/components/ui/table'; import { m } from '$lib/paraglide/messages'; import { listPackageUpdates, streamPackageAction } from '$lib/remotes/packages.remote'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { hasPermission } from '$lib/utils'; type Package = { name: string; version: string }; @@ -22,6 +24,8 @@ const dataResource = $derived(listPackageUpdates(machineId)); const manager = $derived(dataResource.current?.manager ?? ''); const packages = $derived((dataResource.current?.packages ?? []) as Package[]); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('packages', 'write', whoami.current?.permissions)); let search = $state(''); let searchTimer: ReturnType | undefined; @@ -125,7 +129,7 @@ }} > {#snippet actions()} - @@ -161,7 +165,7 @@ {/snippet} - doUpgrade(p.name)}> + doUpgrade(p.name)} disabled={!canWrite}> {m.packages_update_single()} diff --git a/src/routes/dashboard/[machineId]/services/+page.svelte b/src/routes/dashboard/[machineId]/services/+page.svelte index b4f5efb..ffb2efc 100644 --- a/src/routes/dashboard/[machineId]/services/+page.svelte +++ b/src/routes/dashboard/[machineId]/services/+page.svelte @@ -86,17 +86,37 @@ } list = list.filter((s) => { const activeCat = - s.active === 'active' ? 'active' : s.active === 'inactive' ? 'inactive' : s.active === 'failed' ? 'failed' : 'other'; + s.active === 'active' + ? 'active' + : s.active === 'inactive' + ? 'inactive' + : s.active === 'failed' + ? 'failed' + : 'other'; return filterActive[activeCat]; }); list = list.filter((s) => { const loadCat = - s.load === 'loaded' ? 'loaded' : s.load === 'masked' ? 'masked' : s.load === 'not-found' ? 'notFound' : s.load === 'error' ? 'error' : 'loaded'; + s.load === 'loaded' + ? 'loaded' + : s.load === 'masked' + ? 'masked' + : s.load === 'not-found' + ? 'notFound' + : s.load === 'error' + ? 'error' + : 'loaded'; return filterLoad[loadCat as keyof typeof filterLoad] ?? true; }); list = list.filter((s) => { const subCat = - s.sub === 'running' ? 'running' : s.sub === 'exited' ? 'exited' : s.sub === 'dead' ? 'dead' : 'other'; + s.sub === 'running' + ? 'running' + : s.sub === 'exited' + ? 'exited' + : s.sub === 'dead' + ? 'dead' + : 'other'; return filterSub[subCat as keyof typeof filterSub] ?? true; }); list.sort((a, b) => { @@ -120,14 +140,14 @@ bind:search onsearchinput={onSearchInput} onrefresh={() => servicesResource.refresh()} - activeFilterCount={activeFilterCount} + {activeFilterCount} bind:page i18n={{ + display: m.users_filter_display(), + filterTitle: m.services_filter_title(), + next: () => m.users_next(), pageOf: (p) => m.users_page_of(p), previous: () => m.users_prev(), - next: () => m.users_next(), - filterTitle: m.services_filter_title(), - display: m.users_filter_display(), rowsPerPage: m.users_rows_per_page() }} > @@ -232,7 +252,7 @@ {m.services_col_load()} {m.services_col_description()} {/snippet} - {#snippet row(s: ServiceUnit, i: number)} + {#snippet row(s: ServiceUnit)} (null); @@ -185,10 +190,15 @@ title={`${name} · ${m.seo_title_services()}`} description={m.seo_desc_services_detail()} /> -
+
{#snippet failed(err, reset)} - {@const e = err as { body?: { message?: string }; message?: string; status?: number; detail?: string }} + {@const e = err as { + body?: { message?: string }; + detail?: string; + message?: string; + status?: number; + }} {@const status = e.status ?? 0} {@const notFound = status === 404} @@ -282,7 +292,7 @@ + {/snippet} + + +
+
+ + { + const n = Number((e.target as HTMLInputElement).value); + if (n >= 1 && n <= 10000) lines.current = n; + }} + /> +
+
+ + +
+
+ + +
+
+ + +
+
+
+
-
-
- - { - const n = Number((e.target as HTMLInputElement).value); - if (n >= 1 && n <= 10000) lines.current = n; - }} - /> -
-
- - -
-
- - -
-
- - -
-
- -
- {filteredLogs.length} / {entries.length} +
+ {filteredLogs.length} / {entries.length}
-
+

{m.nav_storage()}

diff --git a/src/routes/dashboard/[machineId]/storage/fstab/+page.svelte b/src/routes/dashboard/[machineId]/storage/fstab/+page.svelte index 4d69ac0..7f32c7f 100644 --- a/src/routes/dashboard/[machineId]/storage/fstab/+page.svelte +++ b/src/routes/dashboard/[machineId]/storage/fstab/+page.svelte @@ -10,7 +10,8 @@ import * as Table from '$lib/components/ui/table'; import { m } from '$lib/paraglide/messages'; import { listFstab, removeMount } from '$lib/remotes/storage.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; type FstabEntry = { @@ -24,6 +25,8 @@ const machineId = $derived(pageState.params.machineId!); const fstab = $derived(listFstab(machineId)); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('storage', 'write', whoami.current?.permissions)); let search = $state(''); const filtered = $derived.by(() => { @@ -71,11 +74,11 @@ bind:search onrefresh={() => fstab.refresh()} i18n={{ + display: m.users_filter_display(), + filterTitle: m.users_filter(), + next: () => m.pagination_next(), pageOf: (p) => m.pagination_page_of(p), previous: () => m.pagination_previous(), - next: () => m.pagination_next(), - filterTitle: m.users_filter(), - display: m.users_filter_display(), rowsPerPage: m.users_rows_per_page() }} > @@ -88,7 +91,7 @@ {m.storage_col_pass()} {m.users_actions()} {/snippet} - {#snippet row(e: FstabEntry, i: number)} + {#snippet row(e: FstabEntry)} {e.mountpoint} @@ -113,6 +116,7 @@ { deleting = e; deleteOpen = true; diff --git a/src/routes/dashboard/[machineId]/storage/mounts/+page.svelte b/src/routes/dashboard/[machineId]/storage/mounts/+page.svelte index f8c9e8e..398114d 100644 --- a/src/routes/dashboard/[machineId]/storage/mounts/+page.svelte +++ b/src/routes/dashboard/[machineId]/storage/mounts/+page.svelte @@ -15,22 +15,42 @@ import * as Table from '$lib/components/ui/table'; import { m } from '$lib/paraglide/messages'; import { addMount, listFstab, listMounts, removeMount } from '$lib/remotes/storage.remote'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { PersistedState } from 'runed'; - import { extractErrorMessage } from '$lib/utils'; import { toast } from 'svelte-sonner'; type Mount = { device: string; fstype: string; mountpoint: string; options: string }; const PSEUDO_FSTYPES = new Set([ - 'autofs', 'binfmt_misc', 'bpf', 'cgroup', 'cgroup2', 'configfs', 'debugfs', - 'devpts', 'devtmpfs', 'fusectl', 'hugetlbfs', 'mqueue', 'nsfs', 'proc', - 'pstore', 'ramfs', 'securityfs', 'sysfs', 'tmpfs', 'tracefs' + 'autofs', + 'binfmt_misc', + 'bpf', + 'cgroup', + 'cgroup2', + 'configfs', + 'debugfs', + 'devpts', + 'devtmpfs', + 'fusectl', + 'hugetlbfs', + 'mqueue', + 'nsfs', + 'proc', + 'pstore', + 'ramfs', + 'securityfs', + 'sysfs', + 'tmpfs', + 'tracefs' ]); const id = $props.id(); const machineId = $derived(pageState.params.machineId!); const mounts = $derived(listMounts(machineId)); const fstab = $derived(listFstab(machineId)); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('storage', 'write', whoami.current?.permissions)); let search = $state(''); const filtersStore = new PersistedState('storage.mounts.filters', { showPseudo: false }); @@ -110,18 +130,18 @@ pageSizePresets={[10, 20, 50, 100]} bind:search onrefresh={() => mounts.refresh()} - activeFilterCount={activeFilterCount} + {activeFilterCount} i18n={{ + display: m.users_filter_display(), + filterTitle: m.users_filter(), + next: () => m.pagination_next(), pageOf: (p) => m.pagination_page_of(p), previous: () => m.pagination_previous(), - next: () => m.pagination_next(), - filterTitle: m.users_filter(), - display: m.users_filter_display(), rowsPerPage: m.users_rows_per_page() }} > {#snippet actions()} - @@ -147,7 +167,7 @@ {m.storage_col_options()} {m.users_actions()} {/snippet} - {#snippet row(mt: Mount, i: number)} + {#snippet row(mt: Mount)} {mt.mountpoint} @@ -175,6 +195,7 @@ { deleting = mt; deleteOpen = true; @@ -201,7 +222,12 @@ >
- +
diff --git a/src/routes/dashboard/[machineId]/system/+page.svelte b/src/routes/dashboard/[machineId]/system/+page.svelte index a432152..fa2597a 100644 --- a/src/routes/dashboard/[machineId]/system/+page.svelte +++ b/src/routes/dashboard/[machineId]/system/+page.svelte @@ -26,9 +26,14 @@ -
+ diff --git a/src/routes/dashboard/[machineId]/system/date-time/+page.svelte b/src/routes/dashboard/[machineId]/system/date-time/+page.svelte index 045cab7..e91c0da 100644 --- a/src/routes/dashboard/[machineId]/system/date-time/+page.svelte +++ b/src/routes/dashboard/[machineId]/system/date-time/+page.svelte @@ -18,14 +18,16 @@ setTimezone, systemTime } from '$lib/remotes/system.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; const machineId = $derived(page.params.machineId!); - const time = $derived(systemTime(machineId)); const tzs = $derived(listTimezones(machineId)); const formId = $props.id(); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('system', 'write', whoami.current?.permissions)); let tzOpen = $state(false); let saving = $state(false); @@ -49,7 +51,7 @@ -
+

{m.nav_system_datetime()}

@@ -64,16 +66,34 @@ {m.system_time_current()} - {t.time} - - - - - {m.system_time_timezone()} - {t.timezone} - - + +
+ {m.system_time_timezone()} + {t.timezone} +
+
+ {m.system_time_clock()} + {t.time} +
+
+ NTP + withSaving(() => setNtp({ enabled: v, machineId }))} + /> +
+
+ {t.ntp_synchronized ? m.system_time_ntp_synced() : m.system_time_ntp_not_synced()} +
+
+ {#snippet child({ props })} @@ -82,6 +102,7 @@ role="combobox" aria-expanded={tzOpen} class="w-full justify-between sm:w-80" + disabled={!canWrite} {...props} > {t.timezone} @@ -116,23 +137,6 @@
- - - {m.system_time_ntp()} - {m.system_time_ntp_hint()} - - -
- {t.ntp_synchronized ? m.system_time_ntp_synced() : m.system_time_ntp_not_synced()} -
- withSaving(() => setNtp({ enabled: v, machineId }))} - /> -
-
- {m.system_time_manual()} @@ -159,7 +163,7 @@ disabled={t.ntp} />
- + diff --git a/src/routes/dashboard/[machineId]/system/hostname/+page.svelte b/src/routes/dashboard/[machineId]/system/hostname/+page.svelte index 55b7bd8..1e94dee 100644 --- a/src/routes/dashboard/[machineId]/system/hostname/+page.svelte +++ b/src/routes/dashboard/[machineId]/system/hostname/+page.svelte @@ -7,7 +7,8 @@ import { Label } from '$lib/components/ui/label'; import { m } from '$lib/paraglide/messages'; import { setHostname, systemHostname } from '$lib/remotes/system.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; const machineId = $derived(page.params.machineId!); @@ -15,6 +16,8 @@ const host = $derived(systemHostname(machineId)); const formId = $props.id(); let saving = $state(false); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('system', 'write', whoami.current?.permissions)); // ponytail: hostname syntax is RFC1123 — letters, digits, hyphen, max 63 chars per label. const HOSTNAME_RE = @@ -22,7 +25,7 @@ -
+

{m.nav_system_hostname()}

@@ -73,7 +76,7 @@ maxlength={253} />
- + diff --git a/src/routes/dashboard/[machineId]/system/localization/+page.svelte b/src/routes/dashboard/[machineId]/system/localization/+page.svelte index dadeb1f..e92085d 100644 --- a/src/routes/dashboard/[machineId]/system/localization/+page.svelte +++ b/src/routes/dashboard/[machineId]/system/localization/+page.svelte @@ -20,10 +20,13 @@ setLocale, systemLocale } from '$lib/remotes/system.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; const machineId = $derived(page.params.machineId!); + const whoami = $derived(getWhoami(machineId)); + const canWrite = $derived(hasPermission('system', 'write', whoami.current?.permissions)); const locale = $derived(systemLocale(machineId)); const locales = $derived(listLocales(machineId)); @@ -86,7 +89,7 @@ title={m.seo_title_system_localization()} description={m.seo_desc_system_localization()} /> -
+

{m.nav_system_localization()}

@@ -113,6 +116,7 @@ role="combobox" aria-expanded={open} class="w-full justify-between sm:w-80" + disabled={!canWrite} {...props} > {l.lang} @@ -163,7 +167,7 @@ type="button" class="hover:bg-muted ms-0.5 rounded-sm p-0.5" aria-label="Remove" - disabled={saving} + disabled={saving || !canWrite} onclick={() => (langList = langList.filter((_, j) => j !== i))} > @@ -176,7 +180,7 @@ {#snippet child({ props })} - @@ -210,7 +214,7 @@
- - - +

{m.nav_system_power_desc()}

+
+ + +
| undefined; @@ -205,19 +209,19 @@ bind:search onsearchinput={onSearchInput} onrefresh={() => users.refresh()} - activeFilterCount={activeFilterCount} + {activeFilterCount} bind:page i18n={{ + display: m.users_filter_display(), + filterTitle: m.users_filter_title(), + next: () => m.users_next(), pageOf: (p) => m.users_page_of(p), previous: () => m.users_prev(), - next: () => m.users_next(), - filterTitle: m.users_filter_title(), - display: m.users_filter_display(), rowsPerPage: m.users_rows_per_page() }} > {#snippet actions()} - @@ -273,7 +277,7 @@ {m.users_col_type()} {m.users_actions()} {/snippet} - {#snippet row(u: PamUser, i: number)} + {#snippet row(u: PamUser)} { pwUser = u; pwValue = ''; @@ -314,6 +319,7 @@ { deleting = u; removeHome = false; diff --git a/src/routes/dashboard/[machineId]/users/[username]/+page.svelte b/src/routes/dashboard/[machineId]/users/[username]/+page.svelte index 49b83f3..b71d108 100644 --- a/src/routes/dashboard/[machineId]/users/[username]/+page.svelte +++ b/src/routes/dashboard/[machineId]/users/[username]/+page.svelte @@ -21,7 +21,8 @@ setPamUserGroups, setPamUserPassword } from '$lib/remotes/pam-users.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; import { SvelteSet } from 'svelte/reactivity'; @@ -30,6 +31,8 @@ const machineId = $derived(pageState.params.machineId!); const user = $derived(getPamUser({ machineId, username })); const groups = $derived(listPamGroups(machineId)); + const whoami = $derived(getWhoami(machineId)); + const canRoot = $derived(hasPermission('users', 'root', whoami.current?.permissions)); const primary = $derived((groups.current ?? []).find((g) => g.gid === (user.current?.gid ?? -1))); const supplementary = $derived( @@ -127,11 +130,11 @@

{u.comment || m.users_no_gecos()}

- - @@ -170,7 +173,9 @@
{#if !editing} - + {:else}
@@ -204,7 +208,7 @@ {m.users_col_type()} {m.users_actions()} {/snippet} - {#snippet row(g: PamGroup, i: number)} + {#snippet row(g: PamGroup)} {#if g.members?.length} - {g.members.slice(0, 3).join(', ')}{g.members.length > 3 ? ` +${g.members.length - 3}` : ''} + {g.members.slice(0, 3).join(', ')}{g.members.length > 3 + ? ` +${g.members.length - 3}` + : ''} {:else} — @@ -243,6 +249,7 @@ { deleting = g; deleteOpen = true; diff --git a/src/routes/dashboard/[machineId]/users/groups/[group]/+page.svelte b/src/routes/dashboard/[machineId]/users/groups/[group]/+page.svelte index 65ccdd5..23e31ae 100644 --- a/src/routes/dashboard/[machineId]/users/groups/[group]/+page.svelte +++ b/src/routes/dashboard/[machineId]/users/groups/[group]/+page.svelte @@ -20,7 +20,8 @@ listPamUsers, setPamUserGroups } from '$lib/remotes/pam-users.remote'; - import { extractErrorMessage } from '$lib/utils'; + import { getWhoami } from '$lib/remotes/system.remote'; + import { extractErrorMessage, hasPermission } from '$lib/utils'; import { toast } from 'svelte-sonner'; const id = $props.id(); @@ -28,6 +29,9 @@ const groupName = $derived(pageState.params.group!); const groups = $derived(listPamGroups(machineId)); const users = $derived(listPamUsers(machineId)); + const whoami = $derived(getWhoami(machineId)); + const canDeleteGroup = $derived(hasPermission('groups', 'root', whoami.current?.permissions)); + const canManageMembers = $derived(hasPermission('users', 'root', whoami.current?.permissions)); const group = $derived((groups.current ?? []).find((g) => g.name === groupName)); const members = $derived(group?.members ?? []); @@ -125,7 +129,7 @@

gid {group.gid}

- @@ -140,7 +144,7 @@ {@html m.groups_members_description({ gid: group.gid })}
- @@ -164,7 +168,7 @@