{ "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": "Event Triggers", "logScanRules": "Log Rules", "metricAlertRules": "Metric Alerts", "sharedSecrets": "Shared Secrets", "triggers": "Triggers", "proxies": "Proxies", "events": "Events", "settings": "Settings", "logout": "Log out", "dns": "DNS Records", "containers": "Containers", "sectionObserve": "Observe", "sectionSystem": "System", "closeSidebar": "Close sidebar", "openSidebar": "Open sidebar", "quickNavTitle": "Press 'g' then a letter to jump between sections", "quickNavLabel": "quick-nav" }, "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", "statSubWorkloads": "workloads →", "statSubRunning": "running", "statSubNeedAttention": "need attention", "statSubStale": "stale →" }, "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", "buildCacheCleanupHelp": "Reclaim disk used by Docker build cache from image and static-site builds. Only unused cache is removed — apps redeploying still hit any cache that is still in use.", "pruneBuildCache": "Prune Build Cache", "pruningCache": "Pruning...", "pruneCacheResult": "Removed {count} build-cache records, reclaimed {mb} MB", "pruneBuildCacheConfirmMessage": "This removes unused Docker build-cache layers daemon-wide. Layers still in use are kept, so apps that redeploy soon keep their warm cache; others rebuild from scratch on their next deploy.", "pruneCacheFailed": "Failed to prune build cache", "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", "deleteTitle": "Delete registry?", "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?", "deleteConfirmMessage": "Delete user \"{username}\"? This cannot be undone.", "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", "actions": "Actions", "viewTriggers": "Triggers", "viewTriggersTitle": "View trigger bindings for this workload" }, "common": { "cancel": "Cancel", "confirm": "Confirm", "close": "Close", "toggle": "Toggle", "dismissNotification": "Dismiss notification", "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": { "eyebrowSuffix": "GLOBAL", "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", "eyebrowSuffix": "STALE", "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", "image": "Image", "compose": "Compose", "dockerfile": "Dockerfile", "static_site": "Static Site", "stale_scanner": "Stale Scanner", "stale_cleanup": "Stale Cleanup", "metric_alert": "Metric Alert", "admin": "Admin" }, "metadata": "Details" }, "stats": { "cpu": "CPU", "mem": "MEM", "unavailable": "Stats unavailable" }, "systemHealth": { "containers": "Containers", "proxies": "Proxies", "recentErrors": "Recent Errors" }, "daemons": { "title": "Daemons", "notReachable": "{provider} is not reachable.", "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" } }, "metricalert": { "title": "Metric alert rules", "titleNew": "Forge a new alert", "titleSingular": "Alert rule", "lede": "Threshold checks the watcher runs against each running container's sampled CPU and memory. When a sample crosses the threshold the rule fires into event_log with the rule's severity, where event triggers pick it up and fan out to operator-configured webhooks. {enabled} of {total} enabled.", "ledeNew": "Pick a metric, a comparator, and a threshold. Leave the workload field empty to create a global rule that applies to every workload, or scope it to a single workload.", "stat": { "total": "TOTAL", "global": "GLOBAL", "workload": "WORKLOAD", "enabled": "ENABLED" }, "toolbar": { "newButton": "New alert", "backToList": "Back to alerts" }, "filter": { "all": "ALL", "global": "GLOBAL", "workload": "WORKLOAD" }, "empty": { "heading": "No alert rules yet", "body": "Start with a global rule like CPU greater than 80%, then narrow per-workload by scoping a rule to a single workload.", "cta": "Create the first alert" }, "list": { "name": "Name", "condition": "Condition", "scope": "Scope", "severity": "Severity", "status": "Status", "open": "Open" }, "detail": { "config": "Configuration", "configSub": "id #{id} · scope {scope}", "dangerZone": "Danger zone", "dangerZoneSub": "Deleting an alert rule removes it immediately and stops it from firing.", "deleteButton": "Delete alert", "deleteTitle": "Delete alert rule?", "deleteMessage": "Rule \"{name}\" will be removed immediately and will stop firing." }, "form": { "name": "Name", "namePlaceholder": "e.g. Worker CPU saturated", "condition": "Condition", "metric": "Metric", "comparator": "Comparator", "threshold": "Threshold", "thresholdPlaceholder": "e.g. 80", "thresholdHintPercent": "Percent of the limit (0–100). The rule fires when the sampled value crosses this threshold.", "thresholdHintBytes": "Absolute bytes (e.g. 536870912 for 512 MiB). The rule fires when sampled memory crosses this threshold.", "matchShape": "Match shape", "matchShapeOpts": "SEVERITY · COOLDOWN", "severity": "Severity", "cooldown": "Cooldown (s)", "cooldownHint": "Cooldown is per-rule per-container — the same rule firing on two containers stays independent. It caps how often a sustained breach re-emits to event_log.", "scope": "Scope", "scopeHint": "Workload-scoped rules apply only to that workload's containers. Leave empty to apply the rule to every workload.", "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 alert", "submitting": "Forging…" }, "metric": { "cpu_percent": "CPU %", "memory_percent": "Memory %", "memory_bytes": "Memory (bytes)" }, "metricShort": { "cpu": "CPU", "memory": "Memory" }, "comparator": { "gt": "greater than", "lt": "less than" }, "unit": { "percent": "%", "bytes": "bytes" }, "scope": { "global": "global", "workload": "workload {id}" }, "status": { "enabled": "enabled", "disabled": "disabled" } }, "sharedsecrets": { "eyebrow": "System", "title": "Shared secrets", "titleNew": "Forge a new secret", "titleSingular": "Shared secret", "lede": "Named environment keys the reconciler injects into matching workloads. Scope a secret globally to reach every app, or to a single app to keep it contained. Values are write-only — they are stored once and never returned. {enabled} of {total} enabled.", "ledeNew": "Give the secret an env key and a value. Scope it globally to apply to every app, or to a single app to keep it contained. The value is write-only — once saved it is never shown again.", "stat": { "total": "TOTAL", "global": "GLOBAL", "app": "APP", "enabled": "ENABLED" }, "toolbar": { "newButton": "New secret", "backToList": "Back to secrets" }, "filter": { "all": "ALL", "global": "GLOBAL", "app": "APP" }, "empty": { "heading": "No shared secrets yet", "body": "Start with a global secret like DATABASE_URL, then narrow per-app by scoping a secret to a single app.", "cta": "Create the first secret" }, "list": { "name": "Name", "scope": "Scope", "value": "Value", "encrypted": "Encrypted", "status": "Status", "open": "Open", "valueSet": "set", "valueNone": "—", "encOn": "encrypted", "encOff": "plain" }, "detail": { "config": "Configuration", "configSub": "scope {scope}", "dangerZone": "Danger zone", "dangerZoneSub": "Deleting a shared secret removes it immediately and stops it from being injected.", "deleteButton": "Delete secret", "deleteTitle": "Delete shared secret?", "deleteMessage": "Secret \"{name}\" will be removed immediately and will stop being injected." }, "form": { "name": "Name", "namePlaceholder": "e.g. DATABASE_URL", "nameHint": "The environment variable key injected into matching workloads.", "value": "Value", "valuePlaceholder": "Enter the secret value", "valuePlaceholderEdit": "Leave blank to keep the stored value", "valueHintNew": "The secret value. Stored write-only — once saved it is never shown again.", "valueHintEditSet": "A value is set. Leave blank to keep it, or enter a new value to rotate it.", "valueHintEditUnset": "No value is stored yet. Enter one to set it.", "encrypted": "Encrypted", "encryptedHint": "Encrypted secrets are stored sealed at rest and decrypted only when injected. Disable only for non-sensitive config values.", "encryptedFlipWarning": "Changing the encrypted setting requires re-entering the value. Enter a value above to save.", "scope": "Scope", "scopeGlobalOption": "Global — every app", "scopeAppOption": "App — a single app", "scopeAppEmpty": "No app selected", "scopePick": "Pick app…", "scopePickTitle": "Pick an app", "scopeClear": "Clear app", "scopeSelected": "App", "scopeUnknown": "Unknown app", "scopeHint": "Global secrets apply to every app. App-scoped secrets apply only to that app's workloads.", "description": "Description", "descriptionPlaceholder": "Optional note about what this secret is for", "enabled": "Enabled", "enabledHint": "Disabled secrets stay in the table but are never injected.", "required": "REQUIRED", "optional": "OPTIONAL", "submit": "Forge secret", "submitting": "Forging…", "conflict": "A shared secret with this scope and name already exists." }, "scope": { "global": "Global", "app": "App · {name}" }, "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", "lastFired": "Last fired", "lastFiredNever": "Never fired", "scheduleStatus": "Schedule status", "scheduleStatusSub": "Operational state of the internal scheduler for this trigger. Fire-now skips ahead of the next natural window and resets the cadence to start counting from now.", "fireNow": "Fire now", "fireNowTitle": "Dispatch this trigger immediately and reset the next-fire window.", "fireNowDisabledTitle": "Bind at least one workload before firing.", "firing": "Firing…", "fireConfirmTitle": "Fire schedule trigger?", "fireConfirmMessage": "Trigger \"{name}\" will fire immediately and fan out to its {count} bound workload(s). The next natural fire window will be one full interval from now.", "fireConfirm": "Fire now", "fireResult": "Fired · deployed {deployed}/{bindings} · errored {errored}" }, "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.", "browseImages": "Browse", "browseImagesHint": "Pick an image from a configured registry instead of typing the reference.", "browseImagesTitle": "Select an image", "browseImagesSearch": "Search images…", "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.", "branchPattern": "Branch pattern (preview deploys)", "branchPatternPlaceholder": "feat/* or * for any branch", "branchPatternHint": "When set, any push to a matching branch spawns a per-branch preview deploy. Leave empty to disable previews.", "manualNote": "Manual triggers carry no config. They fire only via the workload's Deploy button or POST /workloads/{id}/deploy.", "scheduleNote": "Fires on a fixed interval driven by Tinyforge's internal scheduler. No external webhook is required — enable the webhook ingress below only if a CI also needs to fire it on demand.", "intervalPresets": "Quick presets", "intervalPreset": { "hourly": "Hourly", "daily": "Daily", "weekly": "Weekly" }, "interval": "Interval", "intervalHint": "Go duration (e.g. \"30m\", \"6h\", \"24h\", \"168h\"). Minimum 1 minute.", "scheduleReference": "Pinned reference (optional)", "scheduleReferencePlaceholder": "stable", "scheduleReferenceHint": "Optional tag, branch, or revision the source plugin should re-pull each fire. Leave empty to let the source use its default.", "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", "wizard": { "stepBasics": "Basics", "stepConfigure": "Configure", "stepTrigger": "Trigger", "stepReview": "Review", "next": "Next", "back": "Back" }, "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", "fieldRequired": "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": "Edit as 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", "imageCpuHint": "Cores, 0 = ∞", "imageMemory": "Memory limit", "imageMemoryHint": "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.", "dockerfileHeader": "dockerfile source · build from a git repo", "dockerfileBuildEyebrow": "build · dockerfile", "dockerfileContextPath": "Build context", "dockerfileContextPathPlaceholder": "(empty = repo root)", "dockerfilePath": "Dockerfile path", "dockerfilePort": "Container port", "dockerfilePortRequired": "Container port is required — pick the port your app listens on (1–65535).", "dockerfileFoot": "Tinyforge clones the repo, builds the image from the Dockerfile, and runs one container. Env vars and volumes live in the 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.", "sourceReportCommitStatus": "Report commit status", "sourceReportCommitStatusDesc": "— report deploy status back to the Git provider as a commit status on the deployed commit.", "staticFoot": "The webhook secret for git push triggers lives on the workload's Webhook panel after creation.", "staticDetectProvider": "Detect", "staticDetectedOk": "Detected: {provider}", "staticDetectedFailed": "Detection failed: {error}", "staticTestConnection": "Test connection", "staticConnectionOk": "Connected", "staticConnectionFailed": "Connection failed: {error}", "staticBrowseRepos": "Browse", "staticBrowseBranches": "Browse branches", "staticBrowseFolders": "Browse folders", "staticPickerRepoTitle": "Select repository", "staticPickerRepoPlaceholder": "Filter repositories…", "staticPickerBranchTitle": "Select branch", "staticPickerBranchPlaceholder": "Filter branches…", "staticFolderRoot": "/ (root)", "staticFolderSelectedPrefix": "Selected folder:", "staticTreeLoading": "Loading folder tree…", "staticTreeEmpty": "No folders found in this branch.", "staticDenoAutoDetected": "Auto-detected an api/ folder — switched to Deno mode.", "imageConflictTag": "IMAGE IN USE", "imageConflictChecking": "Checking for conflicts…", "imageConflictHeading": "{count} workload(s) already use this image:", "imageConflictOpenBtn": "Open", "imageConflictAcknowledgeNote": "If this is intentional (for example a separate stage), continue to create a new workload.", "imageConflictBlockedSubmit": "Conflicts detected for this image — review the list above, then click Create again to proceed.", "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…", "submitAnyway": "Forge anyway", "unsavedChanges": "You have unsaved changes to this app. Leave without creating it?", "unsavedChangesTitle": "Unsaved changes", "unsavedChangesConfirm": "Leave", "errors": { "detectionFailed": "Couldn't detect a Git provider at that URL. Check the base URL is correct and reachable.", "connectionFailed": "Couldn't reach the repository. Check the provider URL, owner/repo, and access token (for private repos).", "reposFailed": "Couldn't list repositories. Check the base URL and access token.", "branchesFailed": "Couldn't list branches. Check the repository and access token.", "treeFailed": "Couldn't load the folder tree. Check the repository, branch, and access token.", "sourceConfigInvalid": "Source config is not valid JSON.", "triggerBindUnknown": "unknown error", "createFailed": "Workload create failed.", "inspectFailed": "Couldn't inspect that image — make sure it's pulled locally and the reference is correct." }, "imageInspect": "Inspect", "imageInspectHint": "Pulls port + healthcheck from the image so you don't have to type them.", "imageInspectOk": "Inspected — port + healthcheck filled.", "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." }, "manifest": { "title": "Manifest", "name": "Name", "source": "Source", "trigger": "Trigger", "publicFace": "Public face", "unnamed": "(unnamed)", "registryPublic": "public registry", "folderRoot": "root", "triggerManual": "Manual only", "internalOnly": "Internal only" } }, "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", "activity": { "title": "Activity", "subtitle": "Recent deploys and events for this app", "empty": "No activity yet. Deploys and events will appear here.", "recentNote": "Showing recent activity.", "loadMore": "Load more", "filterAll": "All", "filterErrors": "Errors", "noErrors": "No errors in the loaded activity." }, "runtimeState": { "title": "Sync status", "sub": "Last successful sync of the source repo and the current container state.", "status": "Status", "lastCommit": "Last commit", "lastSync": "Last sync", "container": "Container", "neverDeployed": "Never deployed. Click Deploy to publish for the first time.", "loading": "Loading runtime state…" }, "storage": { "title": "Persistent storage", "sub": "Mounted at /app/data inside the container.", "used": "Used", "limit": "Limit", "unlimited": "unlimited", "unavailable": "Usage probe unavailable (container may be stopped).", "loading": "Computing usage…" }, "buildLog": { "title": "Build log", "sub": "Live tail of the Docker daemon's build output.", "clear": "Clear" }, "notifications": { "title": "Notification routes", "sub": "Multi-destination fan-out for deploy/build events. Falls back to the workload's legacy URL when empty.", "loading": "Loading routes…", "empty": "No per-workload notification routes configured. Add one to get a per-channel destination.", "addFirst": "Add first route", "add": "Add route", "edit": "Edit route", "delete": "Delete route", "name": "Name", "namePlaceholder": "Slack #alerts", "url": "Webhook URL", "secret": "Signing secret", "secretPlaceholder": "Optional — receiver verifies HMAC if set", "secretEditPlaceholder": "Leave empty to keep the existing secret", "secretHint": "HMAC-SHA256 over the request body, sent as X-Hub-Signature-256.", "eventTypes": "Event types", "eventTypesPlaceholder": "deploy_failure,build_failure (empty = all)", "eventTypesHint": "Comma-separated allow-list. Empty means every event fires this route.", "enabled": "Enabled", "save": "Save route", "saving": "Saving…", "cancel": "Cancel", "allEvents": "all events", "signed": "signed", "disabled": "disabled", "confirmDeleteTitle": "Delete notification route?", "confirmDeleteMessage": "This route will stop firing immediately. The workload's legacy notification URL (if set) will resume catching events when no routes match." }, "snapshots": { "title": "Data snapshots", "sub": "Capture this app's host-bind data volumes as a downloadable archive.", "loading": "Loading snapshots…", "empty": "No snapshots yet.", "coverage": "{count} volume(s) will be captured.", "noCoverage": "No host-bind volumes to snapshot. Snapshots cover image-based apps with absolute, stage, or project volume scopes.", "liveWarning": "Snapshots are taken live — stop the app first for a fully consistent copy of databases.", "skippedTitle": "{count} volume(s) can't be snapshotted yet:", "labelPlaceholder": "Optional label (e.g. before upgrade)", "create": "Snapshot now", "creating": "Capturing…", "created": "Snapshot created.", "createFailed": "Failed to create snapshot", "colLabel": "Label", "colCreated": "Created", "colVolumes": "Volumes", "colSize": "Size", "unlabeled": "(unlabeled)", "download": "Download", "delete": "Delete", "deleted": "Snapshot deleted.", "downloadFailed": "Failed to download snapshot", "deleteFailed": "Failed to delete snapshot", "confirmDeleteTitle": "Delete snapshot?", "confirmDeleteMessage": "This permanently deletes the snapshot archive. This cannot be undone." }, "toolbar": { "stop": "Stop", "start": "Start", "openSite": "Open", "more": "More" }, "liveBadge": { "running": "RUNNING", "transitioning": "TRANSITIONING", "stopped": "STOPPED", "notDeployed": "NOT DEPLOYED", "mixed": "MIXED · {running}/{total} RUNNING" }, "stats": { "title": "Resource usage", "sub": "CPU and memory of the running container.", "subMany": "CPU and memory of each of the {count} containers." }, "webhooks": { "title": "Webhook bindings", "sub": "Triggers wired to this app — manage URLs and signing on the trigger detail page.", "openTrigger": "Open trigger", "disabled": "disabled", "empty": "No triggers bound to this app." }, "errors": { "stopFailed": "Stop failed.", "stopNothing": "Nothing to stop — no running container.", "stopAllFailed": "Stop failed — all containers refused to stop.", "startFailed": "Start failed.", "startNothing": "Nothing to start — deploy first to create a container.", "startAllFailed": "Start failed — all containers refused to start.", "runtimeStateFailed": "Failed to load runtime state.", "storageFailed": "Failed to load storage usage." }, "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.", "editDockerfileHeader": "dockerfile source · build from a git repo", "editDockerfileBuildEyebrow": "build · dockerfile", "editDockerfileContextPath": "Build context", "editDockerfileContextPathPlaceholder": "(empty = repo root)", "editDockerfilePath": "Dockerfile path", "editDockerfilePort": "Container port", "editTestConnection": "Test connection", "editTestConnectionOk": "Connection OK", "editTestConnectionFailed": "Connection failed: {error}", "editTestConnectionUnknownError": "Unknown error", "overrideKeyUnitSingular": "KEY", "overrideKeyUnitPlural": "KEYS", "editTestConnectionIncomplete": "Fill provider, base URL, owner, and name first.", "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.", "previews": { "title": "Preview environments", "subEmpty": "no active previews", "subCountOne": "1 active preview", "subCount": "{count} active previews", "tag": "Preview", "tagTitle": "Per-branch preview deploy of this workload", "armedEmpty": "No active previews — push to a branch matching", "noneEmpty": "No active previews yet.", "open": "Open", "noUrl": "no public URL", "teardown": "Tear down", "teardownTitle": "Tear down preview?", "teardownMessage": "This deletes the preview for branch \"{name}\" and removes its containers and proxy routes. Pushing to the branch again will recreate it.", "teardownConfirm": "Tear down", "teardownPending": "Tearing down…", "teardownFailed": "Teardown failed", "stateRunning": "Running", "statePending": "Pending", "stateStopped": "Stopped", "stateUnknown": "Unknown", "hint": "Previews are created automatically when a push lands on a branch matching a git trigger's branch_pattern, and torn down when the branch is deleted. Each gets its own slug-prefixed subdomain." }, "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" } } } } }