{ "app": { "name": "Tinyforge", "version": "v0.1" }, "layout": { "serviceStatus": "Service status" }, "health": { "connected": "connected", "disconnected": "disconnected", "rawError": "Technical details", "retryNow": "Retry now" }, "nav": { "dashboard": "Dashboard", "apps": "Apps", "eventTriggers": "Triggers", "logScanRules": "Log Rules", "triggers": "Triggers", "proxies": "Proxies", "events": "Events", "settings": "Settings", "logout": "Log out", "dns": "DNS Records", "containers": "Containers" }, "dashboard": { "title": "Dashboard", "newApp": "New app", "totalWorkloads": "Total Workloads", "runningContainers": "Running Containers", "failedContainers": "Failed Containers", "recentWorkloads": "Recent Workloads", "retry": "Retry", "noWorkloads": "No workloads yet.", "noWorkloadsDesc": "Create an app and forge your first workload to get started.", "loadFailed": "Failed to load dashboard", "staleContainers": "Stale Containers", "unusedImagesWarning": "Unused Docker images are taking up disk space", "unusedImages": "unused images", "systemHealth": "System health", "daemons": "Daemons", "systemResources": "System resources", "systemResourcesSubtitle": "CPU, memory, disk, and top consumers" }, "resources": { "cpuCores": "CPU Cores", "memory": "Memory", "running": "Running", "dockerDisk": "Docker Disk", "workloadUtilization": "Workload utilization", "windowMinutes": "{n} minutes", "windowHours": "{n} hours", "noSamples": "No samples yet — the collector samples every {interval}s.", "collectionDisabled": "Stats collection is disabled. Enable it in Settings to populate this chart.", "diskImages": "Images", "diskContainers": "Containers", "diskVolumes": "Volumes", "diskBuildCache": "Build cache", "reclaimable": "{size} reclaimable", "topConsumers": "Top consumers", "byCpu": "by cpu", "byMemory": "by memory", "noRunning": "No running containers.", "instance": "instance", "site": "site", "showHistory": "Show history", "hideHistory": "Hide history", "cpuSeries": "CPU %", "memorySeries": "Memory %", "loading": "Loading…", "sectionTitle": "Resources", "showLogs": "Show logs", "hideLogs": "Hide logs", "dockerUnavailable": "Docker is unavailable. Check that the daemon is running." }, "statsSettings": { "intervalLabel": "Stats collection interval (s)", "intervalHelp": "How often resource samples are collected. 0 disables collection. Range: 5–300s.", "retentionLabel": "Stats retention (hours)", "retentionHelp": "How long resource samples are kept. 0 disables collection. Range: 0–24h." }, "tagPicker": { "registry": "Registry", "local": "Local" }, "settings": { "title": "Settings", "general": "General", "integrations": "Integrations", "dns": "DNS", "maintenance": "Maintenance", "registries": "Registries", "credentials": "Credentials", "authentication": "Authentication", "backup": "Backups", "appearance": "Appearance", "groupMain": "Overview", "groupProxy": "Routing", "groupSystem": "System", "groupSecurity": "Security", "staleThreshold": "Stale threshold (days)", "staleThresholdHelp": "Containers inactive for longer than this will be flagged as stale.", "dockerCleanup": "Docker Image Cleanup", "dockerCleanupHelp": "Remove unused Docker images belonging to your projects. Only images not used by active instances are removed.", "pruneThreshold": "Warning Threshold (MB)", "pruneThresholdHelp": "Show dashboard warning when unused project images exceed this size. 0 = disabled.", "pruneImages": "Prune Unused Images", "pruning": "Pruning...", "pruneResult": "Removed {count} images, reclaimed {mb} MB", "pruneConfirmMessage": "This will remove unused Docker images belonging to your projects. Images used by active instances will not be affected.", "pruneFailed": "Failed to prune images", "proxyProvider": "Proxy Provider", "proxyProviderHelp": "Select how reverse proxy routes are managed for deployed containers.", "proxyNone": "None", "proxyNoneDesc": "No proxy — containers are accessed directly by port", "proxyNpm": "Nginx Proxy Manager", "proxyNpmDesc": "Routes managed via NPM API (configure credentials below)", "npm": "Nginx Proxy Manager", "traefik": "Traefik", "traefikLabelsTitle": "Docker Labels Reference", "traefikLabelsDesc": "These labels are automatically added to deployed containers. Shown here for reference.", "proxyTraefik": "Traefik", "proxyTraefikDesc": "Auto-discovery via Docker labels — no API calls needed", "proxyNoneWarning": "Switching to None does not remove existing proxy routes. You may need to clean them up manually.", "traefikEntrypoint": "Entrypoint", "traefikEntrypointHelp": "Traefik entrypoint name for HTTPS routes", "traefikCertResolver": "Cert Resolver", "traefikCertResolverHelp": "TLS certificate resolver name (e.g., letsencrypt)", "traefikNetwork": "Docker Network", "traefikNetworkHelp": "Network Traefik listens on (leave empty to use global network)", "traefikApiUrl": "Traefik API URL", "traefikApiUrlHelp": "Optional — for health checks (e.g., http://traefik:8080)" }, "settingsGeneral": { "title": "General Settings", "globalConfig": "Global Configuration", "globalConfigDesc": "Core infrastructure: the base domain, network, and polling cadence Tinyforge uses to orchestrate containers.", "configureNpm": "Nginx Proxy Manager is selected.", "configureTraefik": "Traefik is selected.", "configureLink": "Configure provider", "domain": "Domain", "domainHelp": "Base domain for subdomain routing (e.g., example.com → stage-dev-app.example.com)", "serverIp": "Server IP (Docker Host)", "serverIpHelp": "IP of the machine running Docker. Used for NPM remote forwarding.", "publicIp": "Public IP (DNS Target)", "publicIpHelp": "IP for DNS A records — typically your proxy/load balancer. Falls back to Server IP if empty.", "dockerNetwork": "Docker Network", "dockerNetworkHelp": "Docker network that containers and proxy share. Must match your NPM/Traefik network.", "subdomainPattern": "Subdomain Pattern", "subdomainPatternHelp": "Pattern for auto-generated subdomains", "subdomainVarsTitle": "Available variables", "varProject": "Project name", "varStage": "Stage name", "varTag": "Image tag", "varPort": "Container port", "pollingInterval": "Polling Interval (seconds)", "pollingIntervalHelp": "How often to check registries for new tags (60-86400)", "notificationUrl": "Notification URL", "notificationUrlHelp": "Webhook URL for deploy notifications", "saveSettings": "Save Settings", "saving": "Saving...", "saved": "Settings saved successfully", "saveFailed": "Failed to save settings", "loadFailed": "Failed to load settings", "webhookUrl": "Webhook URL", "webhookDesc": "This secret URL receives image push notifications from your CI pipeline.", "noWebhookUrl": "No webhook URL configured", "copy": "Copy", "copied": "Webhook URL copied to clipboard", "regenerateUrl": "Regenerate URL", "regenerating": "Regenerating...", "regenerated": "Webhook URL regenerated", "regenerateFailed": "Failed to regenerate webhook URL", "regenerateWarning": "Warning: regenerating will invalidate the current URL. Update your CI pipelines.", "sslCertificate": "SSL Certificate", "sslCertificateHelp": "Wildcard certificate from NPM for auto-SSL on proxy hosts", "selectCertificate": "Select Certificate", "noCertificate": "None (no SSL)", "clearCertificate": "Clear", "loadingCertificates": "Loading certificates...", "noCertificatesFound": "No wildcard certificates found in NPM", "dnsConfig": "DNS Configuration", "wildcardDns": "Wildcard DNS is configured", "wildcardDnsHelp": "When enabled, all subdomains resolve to your server via a wildcard DNS rule. Disable to manage DNS records per service.", "dnsProvider": "DNS Provider", "dnsProviderHelp": "Select a DNS provider for automatic record management", "cloudflareApiToken": "Cloudflare API Token", "cloudflareApiTokenHelp": "API token with DNS edit permissions for your zone", "cloudflareApiTokenPlaceholder": "Enter Cloudflare API token", "cloudflareApiTokenConfigured": "API token is configured", "cloudflareZone": "Cloudflare Zone", "cloudflareZoneHelp": "Select the DNS zone to manage records in", "selectZone": "Select Zone", "noZone": "No zone selected", "loadingZones": "Loading zones...", "noZonesFound": "No zones found for this token", "testConnection": "Test Connection", "testingConnection": "Testing...", "connectionSuccess": "Connection successful", "connectionFailed": "Connection failed", "baseVolumePath": "Base Volume Path", "baseVolumePathHelp": "Prepended to relative volume sources (e.g., /data + my-app/uploads = /data/my-app/uploads)" }, "settingsRegistries": { "title": "Container Registries", "description": "Manage your container registries for image detection.", "addRegistry": "Add Registry", "editRegistry": "Edit Registry", "addNewRegistry": "Add New Registry", "name": "Name", "nameHelp": "A friendly name for this registry", "url": "URL", "urlHelp": "Registry base URL", "type": "Type", "typeHelp": "Registry type for API compatibility", "token": "Token", "tokenHelpNew": "API token for authentication", "tokenHelpEdit": "Leave empty to keep the existing token", "owner": "Owner", "ownerHelp": "Package owners, comma-separated (e.g., alexei,my-org)", "save": "Save", "saving": "Saving...", "update": "Update", "test": "Test", "testing": "Testing...", "edit": "Edit", "delete": "Delete", "noRegistries": "No registries configured yet.", "addFirst": "Add your first registry", "registryUpdated": "Registry updated", "registryAdded": "Registry added", "registryDeleted": "Registry \"{name}\" deleted", "testSuccess": "Connection to \"{name}\" successful", "saveFailed": "Failed to save registry", "deleteFailed": "Failed to delete registry", "testFailed": "Connection test failed", "loadFailed": "Failed to load registries", "deleteConfirm": "Delete registry \"{name}\"? This cannot be undone.", "healthChecking": "Checking...", "healthConnected": "Connected", "healthUnreachable": "Unreachable" }, "settingsNpm": { "testConnection": "Test Connection", "testing": "Testing...", "testSuccess": "NPM connection successful", "testFailed": "NPM connection failed", "saveFailedConnection": "Cannot save — connection test failed", "remoteMode": "Remote NPM", "remoteModeHelp": "Enable when NPM runs on a different machine than Docker. Forwards to Server IP with published host ports.", "remoteModeWarning": "Requires Server IP in General settings. Ports are auto-mapped to random host ports.", "accessList": "Default Access List", "accessListHelp": "NPM access list for HTTP authentication on proxy hosts. Can be overridden per project.", "noAccessList": "Global default", "selectAccessList": "Select an access list", "noAccessLists": "No access lists found in NPM", "accessListLoadFailed": "Failed to load access lists" }, "settingsCredentials": { "title": "Credentials", "description": "Manage credentials for Nginx Proxy Manager and registry tokens. All values are encrypted at rest.", "npm": "Nginx Proxy Manager", "npmDesc": "Credentials for managing proxy hosts via NPM API", "configured": "Configured", "npmUrl": "NPM URL", "npmUrlHelp": "Nginx Proxy Manager API URL", "email": "Email", "emailHelp": "NPM admin email", "password": "Password", "passwordHelpNew": "NPM admin password (will be encrypted)", "passwordHelpEdit": "Enter the new password to replace the existing one", "changeCredentials": "Change Credentials", "save": "Save", "saving": "Saving...", "saved": "NPM credentials saved", "saveFailed": "Failed to save NPM credentials", "loadFailed": "Failed to load credentials", "registryTokens": "Registry Tokens", "registryTokensDesc": "Registry authentication tokens are managed per-registry in the", "registriesLink": "Registries", "registryTokensSuffix": "section. Each registry stores its token encrypted in the database.", "notSet": "Not set" }, "settingsBackup": { "title": "Backup Management", "description": "Manage database backups and configure automatic backup schedules.", "autoBackup": "Automatic Backups", "autoBackupHelp": "Automatically create backups at the configured interval.", "interval": "Backup Interval", "intervalHelp": "How often to create automatic backups.", "intervalHours": "{hours} hours", "retention": "Retention Count", "retentionHelp": "Maximum number of backups to keep. Oldest are deleted first.", "backupNow": "Backup Now", "creatingBackup": "Creating...", "backupCreated": "Backup created successfully", "backupFailed": "Failed to create backup", "backupList": "Backups", "noBackups": "No backups yet. Create one manually or enable automatic backups.", "columnFilename": "Filename", "columnSize": "Size", "columnType": "Type", "columnDate": "Created", "columnActions": "Actions", "download": "Download", "delete": "Delete", "restore": "Restore", "deleteConfirm": "Are you sure you want to delete this backup?", "deleted": "Backup deleted", "deleteFailed": "Failed to delete backup", "restoreConfirm": "Are you sure you want to restore from this backup? This will replace the current database and restart the server. All current data will be lost.", "restoreWarning": "This action cannot be undone!", "restored": "Database restored. The server is restarting...", "restoreFailed": "Failed to restore backup", "typeManual": "Manual", "typeAuto": "Auto", "typePreDeploy": "Pre-deploy", "preDeploy": "Backup before every deploy", "preDeployHelp": "Take a Tinyforge DB snapshot at the start of every project deploy. Independent of the periodic schedule above; restorable from this list under the \"Pre-deploy\" type.", "save": "Save", "saving": "Saving...", "saved": "Backup settings saved", "saveFailed": "Failed to save backup settings" }, "settingsAuth": { "title": "Authentication Settings", "description": "Configure authentication mode and manage users.", "authMode": "Authentication Mode", "local": "Local (username/password)", "oidc": "OIDC (SSO)", "oidcConfig": "OIDC Provider Configuration", "issuerUrl": "Issuer URL", "clientId": "Client ID", "clientSecret": "Client Secret", "redirectUrl": "Redirect URL", "saveSettings": "Save Settings", "saving": "Saving...", "saved": "Settings saved", "saveFailed": "Failed to save", "loadFailed": "Failed to load settings", "localUsers": "Local Users", "username": "Username", "email": "Email", "role": "Role", "created": "Created", "noUsers": "No users found.", "addUser": "Add User", "viewer": "Viewer", "admin": "Admin", "userCreated": "User created", "userDeleted": "User deleted", "createFailed": "Failed to create user", "deleteFailed": "Failed to delete user", "deleteConfirm": "Are you sure you want to delete this user?", "usernameRequired": "Username and password are required", "networkError": "Network error", "password": "Password" }, "login": { "title": "Tinyforge", "subtitle": "Sign in to your account", "username": "Username", "password": "Password", "signIn": "Sign in", "signingIn": "Signing in...", "or": "or", "ssoButton": "Sign in with SSO (OIDC)", "loginFailed": "Login failed", "networkError": "Network error" }, "proxies": { "title": "Proxy Routes", "description": "Active proxy routes from deployed containers and static sites.", "domain": "Domain", "project": "Project / Site", "stage": "Stage / Mode", "tag": "Tag", "port": "Port", "status": "Status", "source": "Source", "sourceContainer": "Container", "sourceStatic": "Static Site", "sourceDeno": "Deno Site", "filterAll": "All", "filterContainers": "Containers", "filterSites": "Sites", "noRoutes": "No proxy routes", "noRoutesDesc": "Proxy routes are created automatically when you deploy a container with proxy enabled or publish a static site.", "searchPlaceholder": "Search by domain, project, or tag...", "noMatch": "No routes match your search.", "loadFailed": "Failed to load proxy routes", "route": "route", "routes": "routes" }, "common": { "cancel": "Cancel", "confirm": "Confirm", "delete": "Delete", "edit": "Edit", "change": "Change", "save": "Save", "retry": "Retry", "loading": "Loading...", "noData": "No data", "project": "Project", "stack": "Stack", "site": "Site", "back": "Back", "actions": "Actions", "stop": "Stop", "start": "Start", "restart": "Restart", "remove": "Remove", "instance": "instance", "instances": "instances", "next": "Next", "yes": "Yes", "no": "No", "saving": "Saving...", "refresh": "Refresh", "all": "All", "running": "Running", "stopped": "Stopped", "missing": "Missing" }, "containers": { "errLoad": "Failed to load containers", "searchPlaceholder": "Search workload, role, image, subdomain…", "kindFilterLabel": "Workload kind", "stateFilterLabel": "Container state", "emptyTitle": "No containers", "emptyDesc": "Deploy a project, stack, or site to see containers here.", "noMatch": "No containers match the current filters.", "showingN": "Showing {visible} of {total} containers", "col": { "workload": "Workload", "kind": "Kind", "role": "Role", "image": "Image", "state": "State", "subdomain": "Subdomain", "lastSeen": "Last seen" } }, "empty": { "noRegistries": "No registries", "noRegistriesDesc": "Add a container registry to enable image detection.", "noUsers": "No users", "noUsersDesc": "Add local users to manage access." }, "validation": { "required": "{field} is required", "invalidUrl": "Invalid URL format", "invalidDomain": "Invalid domain format", "invalidIp": "Invalid IP format", "invalidEmail": "Invalid email format", "invalidPort": "Port must be between 1 and 65535", "invalidPollingInterval": "Polling interval must be between 60 and 86400 seconds", "invalidProjectName": "Only lowercase letters, numbers, and hyphens allowed", "requiredWhenUpdating": "{field} is required when updating credentials", "requiredForNew": "{field} is required for new registries" }, "theme": { "light": "Light", "dark": "Dark", "system": "System" }, "entityPicker": { "search": "Search...", "noResults": "No results found" }, "stale": { "title": "Stale Containers", "noStale": "No stale containers", "noStaleDesc": "All containers are healthy and running.", "cleanup": "Clean up", "cleanupAll": "Clean up all", "confirmCleanup": "This will stop and remove the container. Continue?", "confirmBulkCleanup": "This will stop and remove all stale containers. Continue?", "daysStale": "days stale", "lastAlive": "Last alive", "count": "Stale", "cleanedUp": "Container cleaned up", "bulkCleanedUp": "{count} containers cleaned up", "cleanupFailed": "Cleanup failed", "loadFailed": "Failed to load stale containers" }, "logs": { "title": "Container Logs", "lines": "lines", "follow": "Follow", "following": "Following...", "loading": "Loading logs...", "noLogs": "No log output" }, "events": { "title": "Event Log", "noEvents": "No events found", "noEventsDesc": "Events will appear here as they occur.", "loadMore": "Load more", "newEvents": "new events", "totalCount": "{count} total", "clearAll": "Clear All", "clearAllTitle": "Clear Event Log", "clearAllMessage": "This will permanently delete all event log entries. This cannot be undone.", "cleared": "Cleared {count} events", "clearFailed": "Failed to clear events", "filter": { "severity": "Severity", "source": "Source", "dateRange": "Date range", "search": "Search events...", "lastHour": "Last hour", "last24h": "Last 24 hours", "last7d": "Last 7 days", "allTime": "All time", "clear": "Clear filters" }, "severity": { "info": "Info", "warn": "Warning", "error": "Error" }, "source": { "deploy": "Deploy", "static_site": "Static Site", "stale_scanner": "Stale Scanner", "stale_cleanup": "Stale Cleanup", "admin": "Admin" }, "metadata": "Details" }, "stats": { "cpu": "CPU", "mem": "MEM", "unavailable": "Stats unavailable" }, "systemHealth": { "title": "System Health", "containers": "Containers", "proxies": "Proxies", "recentErrors": "Recent Errors" }, "daemons": { "title": "Daemons", "refresh": "Refresh", "refreshing": "Refreshing", "docker": "Docker Engine", "npm": "Nginx Proxy Manager", "traefik": "Traefik", "proxy": "Proxy", "online": "Online", "offline": "Offline", "notConfigured": "Not configured", "containers": "Containers", "running": "Running", "paused": "Paused", "stopped": "Stopped", "version": "Version", "apiVersion": "API Version", "platform": "Platform", "kernel": "Kernel", "cpu": "CPU", "memory": "Memory", "storage": "Storage Driver", "images": "Images", "latency": "Latency", "rootDir": "Root Dir", "provider": "Provider", "endpoint": "Endpoint", "proxyHosts": "Proxy Hosts", "managed": "managed", "external": "external", "accessLists": "Access Lists", "certificates": "Certificates", "dockerHint": "Check that the Docker daemon is running and that the socket is reachable.", "proxyHint": "Verify the proxy URL, credentials, and that the service is listening.", "noProxyDesc": "No proxy provider is configured. Tinyforge can manage routes via Nginx Proxy Manager or Traefik.", "configureProxy": "Configure in Settings", "dockerNotReachable": "Docker daemon is not reachable.", "dockerUnreachable": "Docker unreachable", "proxyUnreachable": "Proxy unreachable", "reachable": "reachable" }, "dns": { "title": "DNS Records", "description": "View and manage DNS records created by Tinyforge.", "wildcardActive": "Wildcard DNS Mode Active", "wildcardActiveDesc": "DNS records are managed externally via wildcard DNS. Disable wildcard DNS in Settings to manage records individually.", "refresh": "Refresh", "syncNow": "Sync Now", "syncing": "Syncing...", "syncComplete": "Sync complete: {created} created, {deleted} deleted, {synced} already synced", "syncFailed": "DNS sync failed", "searchPlaceholder": "Search by FQDN...", "allConsumers": "All consumers", "managed": "Managed (instances)", "standalone": "Standalone proxies", "orphaned": "Orphaned", "allStatuses": "All statuses", "statusSynced": "Synced", "statusMissing": "Missing", "statusOrphaned": "Orphaned", "columnFqdn": "FQDN", "columnType": "Type", "columnValue": "Value", "columnConsumer": "Consumer", "columnStatus": "Status", "columnActions": "Actions", "noConsumer": "No consumer", "noRecords": "No DNS records found. Records will appear here when services are deployed.", "noMatchingRecords": "No records match the current filters.", "deleteRecord": "Delete record", "recordDeleted": "DNS record {fqdn} deleted", "deleteFailed": "Failed to delete DNS record", "loadFailed": "Failed to load DNS records", "totalRecords": "Total: {count}", "syncedCount": "Synced: {count}", "missingCount": "Missing: {count}", "orphanedCount": "Orphaned: {count}" }, "language": { "en": "English", "ru": "Russian" }, "timezone": { "eyebrow": "The Forge // Chronograph", "title": "Display timezone", "subtitle": "All dates across Tinyforge — event log, deploys, backups, sites — render in this zone.", "modeLabel": "Detection mode", "modeAuto": "Auto-detect", "modeManual": "Manual", "autoDetect": "Auto-detect from browser", "autoBadge": "Auto", "activeZone": "Active zone", "changeZone": "Change timezone", "clickToChange": "Click to pick a zone →", "pickerTitle": "Select timezone", "pickerPlaceholder": "Search zones — city, region, UTC offset…", "groupAuto": "Detection", "groupPopular": "Popular", "groupAll": "All timezones", "previewFull": "Full timestamp", "previewDate": "Date only", "previewHint": "Timestamps like the event log will look exactly like this." }, "settingsDns": { "title": "DNS Configuration", "description": "Choose whether routes rely on a wildcard record or per-subdomain records managed by a DNS provider." }, "settingsIntegrations": { "title": "Integrations", "outgoing": "Outgoing notifications", "outgoingDesc": "Where Tinyforge posts deploy and alert events. Paste a webhook URL (Apprise, Discord, Slack, your own handler).", "incoming": "Incoming webhooks", "incomingMovedDesc": "Inbound webhooks are now scoped per entity. Open a project or static site to view and rotate its webhook URL." }, "webhookLog": { "title": "Recent webhook deliveries", "description": "The last 14 days of inbound webhook hits — outcome, signature state, and reason. Refreshes every 30 seconds.", "refresh": "Refresh", "loadFailed": "Failed to load webhook deliveries", "empty": "No webhook deliveries yet.", "colTime": "When", "colStatus": "Status", "colOutcome": "Outcome", "colSignature": "Signature", "colDetail": "Detail", "colSource": "Source", "outcome": { "deploy": "Deployed", "skip": "Skipped", "rejected": "Rejected", "not_found": "Not found", "bad_request": "Bad request", "error": "Error" }, "sig": { "valid": "valid", "invalid": "invalid", "missing": "missing", "unconfigured": "off" } }, "webhookPanel": { "copy": "Copy", "copied": "Webhook URL copied to clipboard", "copyFailed": "Failed to copy to clipboard", "noUrl": "No webhook URL configured", "loadFailed": "Failed to load webhook URL", "regenerate": "Regenerate URL", "regenerated": "Webhook URL regenerated", "regenerateFailed": "Failed to regenerate webhook URL", "regenerateWarning": "Regenerating invalidates the current URL. Update any CI pipeline or Git webhook that uses it.", "confirmRegenerate": "Replace the current URL?", "confirmYes": "Regenerate", "confirmNo": "Cancel", "signingTitle": "Inbound HMAC signing", "signingDesc": "Verify webhook payloads with an HMAC-SHA256 signature so a leaked URL alone cannot be used to forge requests. Compatible with Gitea/GitHub webhook secrets.", "signingActive": "Signing secret configured.", "signingInactive": "No signing secret — inbound requests are not authenticated beyond the URL.", "signingIssue": "Issue signing secret", "signingRotate": "Rotate signing secret", "signingDisable": "Disable signing", "signingDisableConfirm": "Disable signing", "signingIssued": "New signing secret issued — copy it before leaving this page", "signingIssueFailed": "Failed to issue signing secret", "signingDisabled": "Signing disabled", "signingDisableFailed": "Failed to disable signing", "signingShownOnce": "Copy this secret now — it will not be shown again.", "signingDismiss": "Dismiss", "signingHint": "Set this as the webhook secret in Gitea/GitHub/GitLab. Tinyforge expects {header} on every request.", "signingCopied": "Signing secret copied to clipboard", "requireSignature": "Require signature", "requireSignatureHelp": "Reject any request that lacks a valid signature. Issue a signing secret first.", "signingRequireFailed": "Failed to update signature requirement" }, "outgoingWebhook": { "signingOn": "Signed", "signingOff": "Unsigned", "signingSecret": "HMAC signing secret", "noSecret": "No signing secret — outgoing events are not signed.", "reveal": "Reveal", "generate": "Generate", "copy": "Copy", "copied": "Signing secret copied to clipboard", "copyFailed": "Failed to copy to clipboard", "loadFailed": "Failed to load signing secret", "regenerate": "Regenerate", "regenerated": "Signing secret regenerated", "regenerateFailed": "Failed to regenerate signing secret", "confirmRegenerateTitle": "Rotate signing secret?", "confirmRegenerate": "The current secret is invalidated immediately. Every receiver verifying it must be updated in lock-step or it will start rejecting events.", "confirmDisableTitle": "Disable HMAC signing?", "confirmDisable": "Future events go out without the X-Hub-Signature-256 header. Receivers that require signatures will reject them.", "confirmYes": "Confirm", "confirmNo": "Cancel", "disable": "Disable signing", "disabled": "Signing disabled", "disableFailed": "Failed to disable signing", "sendTestTitle": "Send a test event", "sendTestHelp": "Fires a synthetic \"test\" event to the resolved URL using the current secret.", "sendTest": "Send test", "sending": "Sending…", "testFailed": "Failed to send test event", "tier": "Tier", "signed": "Signed", "unsigned": "Unsigned", "deliveryId": "Delivery", "responseBody": "Response body", "networkError": "Network error", "fallbackTo": "No URL set on this tier — events will fall through to {label}.", "noUrlConfigured": "No URL set. Configure one above before sending a test." }, "settingsMaintenance": { "title": "Maintenance", "thresholds": "Thresholds", "thresholdsDesc": "Tune when Tinyforge flags stale containers and warns about unused image disk usage.", "dangerZone": "Danger zone" }, "observability": { "section": "Observability", "manage": "manage", "loading": "Loading…", "anyEvent": "any event", "noUrlSet": "No URL configured", "configured": "CONFIGURED", "clear": "Clear", "advanced": "Advanced", "cancel": "Cancel", "save": "Save changes", "saving": "Saving…", "delete": "Delete", "deleting": "Deleting…", "refresh": "Refresh", "open": "Open", "edit": "Edit", "back": "Back", "regex": { "sampleLabel": "Sample line", "placeholder": "paste a representative log line here", "promptType": "type a sample to test the pattern", "noMatch": "NO MATCH", "noMatchHint": "pattern did not match this line", "match": "MATCH", "invalid": "REGEX", "captures": "Captures" } }, "triggers": { "title": "Event triggers", "titleNew": "Forge a new trigger", "titleSingular": "Trigger", "lede": "Filter event-log entries (deploy events, log scanner output, future sources) and dispatch a webhook when they match. Filters AND together; empty filters mean \"match anything.\"", "ledeNew": "Create a filter+action rule. The dispatcher AND-composes all filter fields. Leave a field empty to skip that dimension.", "stat": { "total": "TOTAL", "enabled": "ENABLED", "disabled": "DISABLED" }, "toolbar": { "newButton": "New trigger", "backToList": "Back to triggers" }, "empty": { "heading": "No triggers yet", "body": "Configure a trigger to forward event-log entries to Slack, a notification bridge, or any HTTP receiver. Tinyforge signs requests with X-Hub-Signature-256 when a secret is set.", "cta": "Create the first trigger" }, "list": { "name": "Name", "filters": "Filters", "action": "Action", "status": "Status", "open": "Open" }, "detail": { "config": "Configuration", "configSub": "id #{id} · updated {updatedAt}", "dangerZone": "Danger zone", "dangerZoneSub": "Trigger deletion is immediate. No soft-delete.", "sendTest": "Send test", "sending": "Sending…", "testHttp": "HTTP {code}", "testSigned": "signed", "testOk": "OK", "testFail": "FAIL", "deleteButton": "Delete trigger", "deleteTitle": "Delete trigger?", "deleteMessage": "Trigger \"{name}\" will be removed immediately. This cannot be undone." }, "form": { "name": "Name", "namePlaceholder": "e.g. Slack #alerts on deploy failure", "required": "REQUIRED", "andComposed": "AND-COMPOSED", "filtersLabel": "Filters", "actionLabel": "Action", "actionWebhookBadge": "WEBHOOK", "severityCsv": "Severity (CSV)", "severityPlaceholder": "warn,error", "sourceCsv": "Source (CSV)", "sourcePlaceholder": "deploy,logscan", "messageRegex": "Message regex (optional)", "messageRegexPlaceholder": "(?i)\\bpanic\\b", "invalidRegex": "Invalid regex — server will reject.", "urlLabel": "URL", "urlPlaceholder": "https://hooks.slack.com/services/...", "secretLabel": "HMAC secret (optional)", "secretPlaceholder": "leave blank for unsigned delivery", "secretHint": "Receivers verify X-Hub-Signature-256 against the raw body.", "secretRotateHint": "Stored encrypted at rest. The value is never returned by the API after creation — leave the placeholder untouched to preserve the existing secret, type a new value to rotate, or clear and save to remove signing.", "enabled": "Enabled", "enabledHint": "Disabled triggers stay in the table but never dispatch.", "submit": "Forge trigger", "submitting": "Forging…", "webhookUrl": "Webhook URL" }, "status": { "enabled": "enabled", "disabled": "disabled" } }, "logscan": { "title": "Log scan rules", "titleNew": "Forge a new rule", "titleSingular": "Rule", "lede": "Regex patterns the scanner runs against every running container's log stream. Matched lines land in event_log with the rule's severity, where event triggers pick them up and fan out to operator-configured webhooks. {enabled} of {total} enabled.", "ledeNew": "Tail container logs against a regex. Leave the workload field empty to create a global rule. To override an existing global for one workload, use the per-workload override action on the workload detail page.", "stat": { "total": "TOTAL", "global": "GLOBAL", "workload": "WORKLOAD", "overrides": "OVERRIDES", "activeTails": "ACTIVE TAILS", "droppedBucket": "RATE-LIMITED", "droppedCooldown": "COOLED DOWN", "compileErrors": "COMPILE ERRORS" }, "stats": { "heading": "Scanner stats", "headingSub": "Engine drop counters and last-snapshot compile errors. Counters reset on server restart.", "noCompileErrors": "All rules compile cleanly.", "compileErrorsHeading": "Compile errors (rule dropped from snapshot)", "tailsExplain": "Per-container tail goroutines currently driven by the scanner manager." }, "toolbar": { "newButton": "New rule", "backToList": "Back to rules" }, "filter": { "all": "ALL", "global": "GLOBAL", "workload": "WORKLOAD", "overrides": "OVERRIDES" }, "empty": { "heading": "No rules yet", "body": "Start with a global rule like (?i)\\bpanic\\b at severity error, then narrow per-workload via overrides on the workload detail page.", "cta": "Create the first rule" }, "list": { "name": "Name", "pattern": "Pattern", "scope": "Scope", "severity": "Severity", "streams": "Streams", "status": "Status", "open": "Open" }, "detail": { "config": "Configuration", "configSub": "id #{id} · scope {scope}", "regexTest": "Regex test", "regexTestSub": "Live preview uses the browser's JavaScript regex engine. Click \"Run server test\" to evaluate against Go's RE2 — authoritative and the only reliable signal for RE2-only constructs.", "runServerTest": "Run server test", "testing": "Testing…", "serverTestHint": "Enter a sample line above first", "serverTestSendHint": "Send sample to backend /test endpoint", "serverMatch": "SERVER MATCH", "serverNoMatch": "NO MATCH", "serverNoMatchHint": "server regex did not match the sample", "serverError": "ERROR", "dangerZone": "Danger zone", "dangerZoneSub": "Deleting a global rule cascade-removes its per-workload overrides.", "deleteButton": "Delete rule", "deleteTitle": "Delete rule?", "deleteMessage": "Rule \"{name}\" will be removed immediately. Per-workload overrides referencing it will be cascade-deleted." }, "form": { "name": "Name", "namePlaceholder": "e.g. Panic in worker", "pattern": "Pattern", "regex": "REGEX", "patternPlaceholder": "(?i)\\bpanic\\b", "invalidRegex": "Invalid regex — server will reject.", "matchShape": "Match shape", "matchShapeOpts": "SEVERITY · STREAMS · COOLDOWN", "severity": "Severity", "streams": "Streams", "cooldown": "Cooldown (s)", "cooldownHint": "Cooldown is per-rule per-container — the same rule firing on two containers stays independent. Token bucket caps per-container emissions at 10 events / 60s to prevent flooding event_log.", "scope": "Scope", "scopePlaceholder": "empty for global rule, or paste a workload id", "scopeHint": "Workload-scoped rules apply only to that workload's containers. Per-workload overrides are easier to create from the workload detail page.", "scopeGlobal": "Global (applies to every workload)", "scopePick": "Pick workload…", "scopePickTitle": "Pick a workload", "scopeClear": "Make global", "scopeSelected": "Workload", "scopeUnknown": "Unknown workload", "enabled": "Enabled", "enabledHint": "Disabled rules stay in the table but never fire.", "required": "REQUIRED", "optional": "OPTIONAL", "submit": "Forge rule", "submitting": "Forging…" }, "scope": { "global": "global", "workload": "workload {id}", "override": "override of #{id}", "overrideShort": "override #{id}" }, "status": { "enabled": "enabled", "disabled": "disabled", "on": "on", "off": "off" }, "panel": { "heading": "Log rules", "subEmpty": "No effective rules for this workload", "subCount": "{count} effective rules", "subCountOne": "1 effective rule", "emptyHint": "This workload has no log scan rules applied. Create one via New rule — globals apply automatically; this workload can also have its own narrower rules or overrides.", "newRule": "New rule", "footerHint": "Global rules apply to every workload. Workload rules apply only here. Override rows substitute for a global on this workload — edit them to disable or change severity per-workload without touching the global.", "override": "Override", "overriding": "Overriding…", "overrideTitle": "Create a per-workload override of this global rule" } }, "redeployTriggers": { "section": "The Forge", "title": "Redeploy triggers", "titleNew": "Forge a new trigger", "titleSingular": "Trigger", "lede": "Sources of redeploy signals — registry pushes, git events, manual fires, schedules, webhooks, log matches. Each trigger lives once and fans out to every workload bound to it.", "ledeNew": "Pick a kind, name it, and decide whether external systems may poke it via webhook. Bind it to one or more workloads from the workload page after creation.", "ledeDetail": "Edit the trigger config, manage its webhook ingress, and review every workload listening to this signal.", "stat": { "total": "TOTAL", "byKind": "{kind}", "withWebhook": "WEBHOOK ON", "boundWorkloads": "WORKLOADS" }, "kind": { "registry": "Registry", "git": "Git", "manual": "Manual", "schedule": "Schedule", "webhook": "Webhook", "logscan": "Log scan", "unknown": "Unknown" }, "kindShort": { "registry": "REG", "git": "GIT", "manual": "MAN", "schedule": "CRN", "webhook": "HK", "logscan": "LOG", "unknown": "?" }, "kindHint": { "registry": "Watch a container image; fire when a new tag matching the pattern is pushed.", "git": "Fire when a configured branch advances or a tag matching the pattern is created.", "manual": "Fires only via the workload's Deploy button or POST /workloads/{id}/deploy.", "schedule": "Fires on a fixed cron-style schedule.", "webhook": "Pure webhook — fires when the ingress URL is hit.", "logscan": "Fires when an upstream log-scan rule matches a tailed line.", "unknown": "Unknown trigger kind — fall back to the raw JSON editor." }, "toolbar": { "newButton": "New trigger", "backToList": "Back to triggers" }, "filter": { "all": "ALL", "ariaLabel": "Filter by kind" }, "empty": { "heading": "No triggers yet", "body": "A trigger is the source of a redeploy signal — a registry watcher, git hook, manual button, scheduled fire, or webhook. Create one and bind it to as many workloads as you like.", "cta": "Forge the first trigger" }, "list": { "name": "Name", "kind": "Kind", "bindings": "Workloads", "webhook": "Webhook", "created": "Created", "open": "Open", "webhookOn": "ON", "webhookOff": "—", "noBindings": "—", "bindingsCount": "{count}" }, "detail": { "config": "Trigger configuration", "configSub": "kind {kind} · id {id} · updated {updatedAt}", "webhook": "Webhook ingress", "webhookSub": "When enabled, external systems can fire this trigger by posting to the URL below. Each workload bound to it will be redeployed in turn.", "webhookEnable": "Enable webhook ingress", "webhookEnableHint": "When off, the trigger fires only via internal sources (its kind config) and the manual deploy button.", "webhookRequireSig": "Require HMAC signature", "webhookRequireSigHint": "Reject requests without a valid X-Hub-Signature-256 header. Recommended whenever the URL is reachable from the public internet.", "webhookUrlLabel": "Ingress URL", "webhookUrlNote": "Paste this into your CI / registry / GitHub webhook settings. The secret segment is the bearer — treat it like a password.", "webhookCopy": "Copy", "webhookCopied": "Copied", "webhookRotate": "Rotate secret", "webhookRotating": "Rotating…", "webhookDisabledNote": "Webhook ingress is disabled. Toggle it on, save, and the URL will appear here.", "bindings": "Bound workloads", "bindingsSub": "Every workload listening to this trigger. To bind a new workload, open the workload page and add this trigger from there.", "bindingsEmpty": "No workloads are bound to this trigger yet. Open a workload and bind this trigger from its Triggers panel.", "bindingsListItem": { "openWorkload": "Open workload", "unbind": "Unbind" }, "bindingEnabledHint": "Disable to keep the binding but stop this trigger from redeploying that workload.", "dangerZone": "Danger zone", "dangerZoneSub": "Trigger deletion is immediate. All bindings to it are cascade-removed.", "deleteButton": "Delete trigger", "deleteTitle": "Delete trigger?", "deleteMessage": "Trigger \"{name}\" will be removed immediately, along with {count} binding(s). This cannot be undone.", "rotateTitle": "Rotate webhook secret?", "rotateMessage": "The current ingress URL stops working immediately. Update every external integration with the new URL after rotation.", "rotateConfirm": "Rotate now", "unbindTitle": "Unbind workload?", "unbindMessage": "Workload \"{name}\" will stop redeploying when this trigger fires. The workload itself is not deleted.", "unbindConfirm": "Unbind" }, "form": { "kindLabel": "Kind", "kindHint": "Pick the source of the redeploy signal. The form below adapts to the kind.", "name": "Name", "namePlaceholder": "e.g. ghcr.io/me/api · main", "required": "REQUIRED", "configLabel": "Configuration", "image": "Image reference", "imagePlaceholder": "registry.example.com/owner/app", "imageHint": "Full image reference without the tag — Tinyforge matches new tags pushed under it.", "tagPattern": "Tag pattern", "tagPatternPlaceholder": "*", "tagPatternHint": "path.Match glob (e.g. v*, release-*). Use * to match every tag.", "repo": "Repository", "repoPlaceholder": "owner/name", "repoHint": "Provider-agnostic owner/name slug as advertised by the git host.", "mode": "Mode", "modePush": "Push to branch", "modeTag": "Tag created", "branch": "Branch", "branchPlaceholder": "main", "branchHint": "Only push events advancing this branch fire the trigger.", "manualNote": "Manual triggers carry no config. They fire only via the workload's Deploy button or POST /workloads/{id}/deploy.", "unknownNote": "This kind has no built-in form yet. Use the JSON editor below; the server validates the shape.", "advancedToggle": "Advanced JSON", "advancedHint": "Power-user fallback — replaces the structured form with the raw config payload.", "configJson": "Config JSON", "configJsonHint": "Must parse as a valid JSON object. The shape is validated server-side per kind.", "invalidJson": "Invalid JSON — server will reject.", "webhookEnabled": "Enable webhook ingress now", "webhookEnabledHint": "Generates a secret URL that external systems can hit to fire the trigger.", "webhookRequireSig": "Require HMAC signature", "webhookRequireSigHint": "Reject unsigned requests. The secret is the same one embedded in the URL — sign the body with HMAC-SHA256 and send it as X-Hub-Signature-256.", "submit": "Forge trigger", "submitting": "Forging…", "cancel": "Cancel" }, "binding": { "enabled": "Enabled", "disabled": "Disabled" } }, "apps": { "list": { "eyebrowSuffix": "APPS", "title": "Apps", "ledePrefix": "Plugin-native deployables —", "ledeKindImage": "image", "ledeKindCompose": "compose", "ledeKindStatic": "static", "ledeMiddle": ", or", "ledeSuffix": ", with pluggable redeploy triggers. Legacy projects, stacks, and sites continue to live under their own sections during the cutover.", "statTotal": "TOTAL", "statImage": "IMAGE", "statCompose": "COMPOSE", "statStatic": "STATIC", "refresh": "Refresh", "newApp": "New App", "filterAriaLabel": "Filter by source plugin", "filterAll": "ALL", "loadError": "Failed to load apps", "alertTag": "ERR", "emptyTitle": "No apps yet", "emptyBody": "Apps unify image, compose, and static deployables behind a single plugin-driven surface. Forge your first one to see it light up here.", "emptyCta": "Forge the first app", "colName": "Name", "colSource": "Source", "colTrigger": "Trigger", "colCreated": "Created", "colActions": "Actions", "rowOpen": "Open" }, "new": { "pageTitle": "New App · Tinyforge", "backLabel": "Back to apps", "eyebrowSuffix": "NEW APP", "title": "Forge a new app", "ledePrefix": "Create a plugin-native workload.", "ledeSourceLabel": "Source", "ledeSourceMid": "= how it deploys (image, compose, static). Pick or create a", "ledeTriggerLabel": "trigger", "ledeSuffix": "below — when one fires, the source plugin redeploys.", "loadingKinds": "Loading available plugin kinds…", "alertTag": "ERR", "fieldName": "Name", "fieldNameRequired": "REQUIRED", "fieldNamePlaceholder": "my-app", "fieldNameHint": "Lowercase, no spaces. Becomes part of container names and subdomains.", "fieldSourcePlugin": "Source plugin", "fieldSourceLabel": "Source", "fieldSourceHint": "Populated from the running daemon — only plugins compiled in show up. Triggers (registry / git / manual) are configured below as standalone records.", "fieldSourceConfig": "Source config", "fieldConfigYaml": "YAML", "fieldConfigForm": "FORM", "fieldConfigJson": "JSON", "advancedJson": "Advanced JSON", "backToForm": "Back to form", "resetSample": "Reset sample", "switchToJsonTitle": "Switch to the raw JSON editor", "switchToFormTitle": "Switch back to the form", "jsonOk": "JSON OK", "jsonInvalid": "JSON INVALID", "linesUnit": "lines", "composeHeader": "compose.yaml · compose", "composeAriaLabel": "Compose YAML", "composeProjectLabel": "Compose project name (optional)", "composeProjectPlaceholder": "(defaults to sanitized workload name)", "composePlaceholder": "services:\n web:\n image: nginx:alpine\n ports:\n - \"80\"", "imageHeader": "image source · runtime knobs", "imageRefLabel": "Image (registry path)", "imageRefPlaceholder": "registry.example.com/owner/app", "imageRefHint": "Fully-qualified reference; the tag is set per-deploy via the trigger or the Default tag field below.", "imagePort": "Port", "imageHealthcheck": "Healthcheck path", "imageDefaultTag": "Default tag", "imageRegistryLabel": "Registry (for private pulls)", "imageRegistryPublic": "(public — no auth)", "imageRegistryHint": "Match the name from the Registries settings page. Leave empty for public images.", "imageCpu": "CPU limit (cores, 0 = ∞)", "imageMemory": "Memory limit (MB, 0 = ∞)", "imageMax": "Max instances", "imageMaxHint": "1 = strict blue-green.", "imageFoot": "Env vars and volume mounts live in their own panels on the workload detail page after creation.", "staticHeader": "static source · pages from a repo", "staticProvider": "Provider", "staticBaseUrl": "Base URL", "staticBaseUrlPlaceholder": "https://git.example.com", "staticRepoOwner": "Repo owner", "staticRepoOwnerPlaceholder": "owner", "staticRepoName": "Repo name", "staticRepoNamePlaceholder": "pages", "staticBranch": "Branch", "staticBranchPlaceholder": "main", "staticFolder": "Folder path (optional)", "staticFolderPlaceholder": "(repo root)", "staticToken": "Access token (private repos)", "staticTokenPlaceholder": "(leave blank for public repos)", "staticTokenHint": "Encrypted at rest. Required only when the repo is private.", "staticMode": "Mode", "staticModeStaticDesc": "— serve files via nginx; zero runtime overhead.", "staticModeDenoDesc": "— Deno runtime container with optional dynamic routing.", "staticRenderMarkdown": "Render markdown", "staticRenderMarkdownDesc": "— auto-render .md files as HTML pages.", "staticFoot": "The webhook secret for git push triggers lives on the workload's Webhook panel after creation.", "sourceConfigJsonTitle": "source_config.json · {kind}", "sourceConfigJsonAria": "Source plugin configuration (JSON)", "triggerNumLabel": "Trigger", "triggerNumOptional": "OPTIONAL", "triggerNewTag": "NEW", "triggerPickTag": "PICK", "triggerSkipTag": "SKIP", "noteSkipTag": "SKIP", "noteEmptyTag": "∅", "faceLabel": "Public face", "faceOptional": "OPTIONAL", "faceSubdomain": "Subdomain", "faceSubdomainPlaceholder": "myapp", "faceDomain": "Domain", "faceDomainPlaceholder": "(inherit from settings)", "facePort": "Target port", "faceHint": "Leave blank to skip provisioning a proxy route. Filling any field creates a single face row attached to this workload.", "cancel": "Cancel", "submit": "Forge app", "submitting": "Forging…", "triggers": { "section": "Trigger", "sectionSub": "Optional. Pick how this app gets a redeploy signal — registry watcher, git event, or manual button.", "modeInline": "Add a trigger", "modeInlineHint": "Creates a brand-new trigger record bound to this app — fits the common 1:1 case.", "modePick": "Pick existing trigger", "modePickHint": "Bind an existing trigger so multiple apps share one signal.", "modeSkip": "Skip — add later", "modeSkipHint": "The app is created without any trigger binding. Manual deploys still work.", "switchToPick": "Pick existing trigger →", "switchToInline": "← Create a new trigger instead", "switchToSkip": "Skip for now", "pickPlaceholder": "Select a trigger…", "pickEmpty": "No triggers exist yet — create one inline above, or visit /triggers.", "pickLabel": "Existing trigger", "pickHint": "The same trigger can be bound to many apps. Manage standalone triggers under /triggers.", "pickWebhookOn": "WEBHOOK ON", "skippedNote": "No trigger will be bound. You can add one from the app's Triggers panel after it's created.", "bindError": "App created, but the trigger binding failed: {error}. Open the app's Triggers panel to retry." } }, "detail": { "pageTitleFallback": "App", "backLabel": "Back to apps", "eyebrowSuffix": "APP", "kickerId": "id: {id}", "loading": "Loading workload…", "loadError": "Failed to load app", "deployError": "Deploy failed", "saveError": "Save failed", "deleteError": "Delete failed", "alertTag": "ERR", "createdAt": "created", "refreshLabel": "Refresh", "editButton": "Edit", "deleteButton": "Delete", "editTitle": "Edit configuration", "editSubPrefix": "Source", "editSubSuffix": "· triggers managed in the Triggers panel below", "editFieldName": "Name", "editFieldParent": "Parent workload", "editFieldOptional": "OPTIONAL", "editFieldParentPlaceholder": "workload UUID (blank for root)", "editSourceConfig": "Source config", "editConfigYaml": "YAML", "editConfigForm": "FORM", "editConfigJson": "JSON", "advancedJson": "Advanced JSON", "backToForm": "Back to form", "switchToJsonTitle": "Switch to the raw JSON editor", "switchToFormTitle": "Switch back to the form", "jsonOk": "JSON OK", "jsonInvalid": "JSON INVALID", "editComposeProject": "Compose project name (optional)", "editComposeProjectPlaceholder": "(defaults to sanitized workload name)", "editComposePlaceholder": "services:\n web:\n image: nginx:alpine", "editComposeAria": "Compose YAML", "editComposeHeader": "compose.yaml", "editImageHeader": "image source · runtime knobs", "editImageRef": "Image (registry path)", "editImageRefPlaceholder": "registry.example.com/owner/app", "editImagePort": "Port", "editImageHealthcheck": "Healthcheck path", "editImageDefaultTag": "Default tag", "editImageRegistry": "Registry (for private pulls)", "editImageRegistryPublic": "(public — no auth)", "editImageCpu": "CPU limit (cores, 0 = ∞)", "editImageMemory": "Memory limit (MB, 0 = ∞)", "editImageMax": "Max instances", "editImageFoot": "Env vars and volume mounts use their own panels below — saving here preserves them.", "editStaticHeader": "static source · pages from a repo", "editStaticProvider": "Provider", "editStaticBaseUrl": "Base URL", "editStaticBaseUrlPlaceholder": "https://git.example.com", "editStaticRepoOwner": "Repo owner", "editStaticRepoName": "Repo name", "editStaticBranch": "Branch", "editStaticFolder": "Folder path (optional)", "editStaticFolderPlaceholder": "(repo root)", "editStaticToken": "Access token (private repos)", "editStaticTokenPlaceholder": "(leave blank for public repos)", "editStaticMode": "Mode", "editStaticModeStaticDesc": "— serve files via nginx.", "editStaticModeDenoDesc": "— Deno runtime with dynamic routing.", "editStaticRenderMarkdown": "Render markdown", "editStaticRenderMarkdownDesc": "— auto-render .md as HTML.", "editSourceJsonHeader": "source_config.json", "editSourceJsonAria": "Source plugin configuration (JSON)", "editPublicFaces": "Public faces", "editPublicFacesTag": "JSON ARRAY", "editPublicFacesHeader": "public_faces.json", "editPublicFacesAria": "Public faces configuration (JSON array)", "editCancel": "Cancel", "editSave": "Save changes", "editSaving": "Saving…", "manualDeployTitle": "Manual deploy", "manualDeployOk": "OK", "manualDeployDispatched": "Dispatched {reference} as {by}", "manualDeployRefAria": "Deploy reference", "manualDeployRefPlaceholder": "reference (image tag, git sha, blank for default)", "manualDeployButton": "Deploy", "manualDeployDispatching": "Dispatching…", "manualDeployHint": "Use a specific image tag, git sha, or branch to force a deploy. Leave blank to use the default reference resolved by the source plugin.", "containersTitle": "Containers", "containersEmpty": "No containers yet", "containersCount": "{count} reconciled", "containersEmptyInline": "No containers yet — deploy to spin one up.", "containersColRole": "Role", "containersColState": "State", "containersColImage": "Image", "containersColSubdomain": "Subdomain", "containersColLastSeen": "Last seen", "containersColActions": "Actions", "containersLogsAction": "Logs", "chainTitle": "Chain", "chainSubFromParent": "promotes from a parent", "chainSubParentOf": "parent of", "chainChildSingular": "child", "chainChildPlural": "children", "chainParentLabel": "Parent", "chainSelfLabel": "This", "chainChildrenLabel": "Children", "chainPromoteButton": "Promote from parent", "chainPromoting": "Promoting…", "chainHint": "Set parent_workload_id on a workload to build a chain. Image-source children can promote the parent's currently-running tag with one click.", "volumesTitle": "Volumes", "volumesEmpty": "No mounts", "volumesCountSingular": "{count} mount", "volumesCountPlural": "{count} mounts", "volumesColTarget": "Target", "volumesColSource": "Source", "volumesColScope": "Scope", "volumesColUpdated": "Updated", "volumesColActions": "Actions", "volumeSource": "Source (host)", "volumeSourcePlaceholder": "/srv/data/myapp", "volumeTarget": "Target (container)", "volumeTargetPlaceholder": "/data", "volumeScope": "Scope", "volumeAddButton": "Add / Replace", "volumeSaving": "Saving…", "volumeHint": "Absolute mounts bind a host path into the container. Non-absolute scopes are accepted for future use; only absolute is honoured at deploy time today.", "volumeTargetError": "Target must be an absolute container path (e.g. /data)", "volumeSetFailed": "Failed to set volume", "volumeDeleteFailed": "Failed to delete volume", "envTitle": "Env", "envEmpty": "No overrides", "envCountSingular": "{count} override", "envCountPlural": "{count} overrides", "envColKey": "Key", "envColValue": "Value", "envColUpdated": "Updated", "envColActions": "Actions", "envEncrypted": "ENCRYPTED", "envKey": "Key", "envKeyPlaceholder": "DATABASE_URL", "envValue": "Value", "envValuePlaceholder": "(empty to unset)", "envEncryptLabel": "Encrypt at rest", "envAddButton": "Add / Replace", "envSaving": "Saving…", "envHint": "Encrypted values are write-only after store — the API redacts them on read. Rotate by setting a new value.", "envKeyRequired": "Key is required", "envSetFailed": "Failed to set env", "envDeleteFailed": "Failed to delete env", "sourceConfigTitle": "Source config", "sourceConfigCopy": "Copy", "sourceConfigCopied": "Copied", "sourceConfigCopyAria": "Copy source config", "publicFacesTitle": "Public faces", "publicFacesCopyAria": "Copy public faces", "deleteConfirmTitle": "Delete this app?", "deleteConfirmMessage": "Tears down all containers and proxy routes owned by \"{name}\", then removes the row. This cannot be undone.", "deleteConfirmFallbackName": "this workload", "deleteConfirmYes": "Yes, delete", "deleteConfirmDeleting": "Deleting…", "manualDeploySub": "Bypasses configured triggers and dispatches through the source plugin directly.", "chainTriggersZero": "no triggers", "chainTriggersOne": "1 trigger", "chainTriggersMany": "{count} triggers", "bindings": { "title": "Triggers", "subEmpty": "No triggers bound. Manual deploys still work — add a trigger to wire up registry / git / webhook redeploys.", "subCount": "{count} trigger bound", "subCountMany": "{count} triggers bound", "addButton": "Add trigger", "openTrigger": "View trigger", "unbindAction": "Unbind", "rowEnabled": "Enabled", "rowDisabled": "Disabled", "rowEnableHint": "Disable to keep the binding but stop this trigger from redeploying the app.", "loading": "Loading triggers…", "loadError": "Failed to load trigger bindings", "unbindTitle": "Unbind trigger?", "unbindMessage": "Trigger \"{name}\" will stop redeploying this app. The trigger record itself is not deleted — it stays in /triggers and remains bound to any other apps.", "unbindConfirm": "Unbind", "modal": { "title": "Add trigger", "subtitle": "Bind a trigger to this app — create a new one inline, or pick an existing trigger to share.", "tabInline": "Create new", "tabPick": "Bind existing", "submitInline": "Create & bind", "submitPick": "Bind", "submitting": "Binding…", "cancel": "Cancel", "error": "Bind failed", "pickPlaceholder": "Select a trigger…", "pickEmpty": "No triggers exist yet — switch to \"Create new\" to make one.", "pickLabel": "Existing trigger", "pickKind": "Filter by kind", "pickKindAll": "All kinds" }, "override": { "toggle": "Override", "title": "Per-binding overrides", "subtitle": "Override fields of the trigger's config for this app only. Top-level keys you set here win; everything else inherits from the trigger.", "badgeOne": "OVERRIDES 1 FIELD", "badgeMany": "OVERRIDES {count} FIELDS", "badgeTitle": "This binding overrides one or more fields of the trigger's config.", "baseLabel": "Trigger config", "baseLoading": "Loading trigger config…", "baseHint": "Read-only view of the parent trigger's config. Edit it from the trigger page if it should change for every binding.", "editLabel": "Override (JSON object)", "editHint": "Top-level merge: only keys present here override the trigger. Leave the editor as {} to inherit verbatim.", "previewLabel": "Effective config", "previewHint": "Preview of what this binding will see when the trigger fires (trigger config with the override merged on top).", "invalidJson": "Override must be a JSON object.", "tooLarge": "Override is {size} B — exceeds the {limit} B server limit.", "errInvalidJson": "Cannot save: override is not a valid JSON object.", "errTooLarge": "Cannot save: override exceeds the 8 KiB server limit.", "saveButton": "Save override", "saving": "Saving…", "resetButton": "Reset to inherit", "closeButton": "Close" } } } } }