feat(server-s3): one Linux user per account (S3) — experimental
Server mode can now run each account as its own Linux user (ah-<name>) when `agenthub server install-system` has set S3 up. The dashboard reaches a member only through sudo -u / write-as / pane-launch, reads a member's files only through owner- and type-checked opens, and hooks are authenticated by peer uid. Local mode and server mode without S3 are unchanged. EXPERIMENTAL: tested with fake sudo and fake accounts only. It has not yet been verified on a real install with real member accounts, sudo or a real Codex login. Do not offer it to real users yet. - S3 core: provisioning, per-member tmux servers, the proxy key gateway, shared tools in /opt, dropped capabilities for children (P0–P5) - P4: plan and quota per member home - P6: ~/.claude.json seed; opt-in plugin baseline installed as the member; managed-settings spike script (not run) - P7: Codex account pool (`agenthub accounts add --tool codex`) - Roles merged with main's admin / analyst / developer: analysts write only their own rows, under S3 as well - Per-account prefs: one implementation (Chat UI + onboarding) - Security reviews F1–F49 (five rounds), each fix with a regression test: symlink/TOCTOU-safe member reads, no host writes in a member home, credentials and the data dir never served, owner and folder account must agree, pane tokens bound to the pane's uid, bounded sudo/tmux/git calls and member reads (no hang or crash from a member), async route rejections answer 500 instead of exiting - Docs: docs/SERVER-MODE.md (S3 model, accepted ceilings, operator runbook), docs/plans/server-mode/S3-PLAN.md, CLAUDE.md S3 rules Tests: server 2838/2841 (3 need 127.0.0.1:3000, held on this box); client 1162/1162; tsc clean; headers check passes. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"brand": "AgentHub",
|
||||
"progress": "{{n}} of {{total}}",
|
||||
"skip": "Skip setup",
|
||||
"back": "Back",
|
||||
"continue": "Continue",
|
||||
"finish": "Add your first workspace",
|
||||
"agent": {
|
||||
"eyebrow": "Welcome to AgentHub",
|
||||
"title": "Pick your default agent",
|
||||
"body": "AgentHub works with every CLI agent. Choose the one you'll reach for most. Switch any time.",
|
||||
"detected": "Detected on this machine · {{count}}",
|
||||
"none": "No AI tool is installed yet. An admin installs them in Settings → AI Tools; you can finish setup and pick one later.",
|
||||
"more": "Show {{count}} more agents",
|
||||
"less": "Show fewer",
|
||||
"notInstalled": "not installed",
|
||||
"permission": "Start new sessions in",
|
||||
"permissionHint": "Bypass permissions is never a default: it stays a red switch you turn on for one session.",
|
||||
"modes": {
|
||||
"auto": "Auto",
|
||||
"acceptEdits": "Accept edits",
|
||||
"default": "Ask first",
|
||||
"plan": "Plan"
|
||||
}
|
||||
},
|
||||
"look": {
|
||||
"title": "Make it feel like home",
|
||||
"body": "Pick the look you want to stare at for hours.",
|
||||
"modes": {
|
||||
"system": "System",
|
||||
"dark": "Dark",
|
||||
"light": "Light"
|
||||
},
|
||||
"modeHints": {
|
||||
"system": "Match OS",
|
||||
"dark": "Easy on the eyes",
|
||||
"light": "Bright & crisp"
|
||||
},
|
||||
"darkThemes": "Dark theme",
|
||||
"lightThemes": "Light theme",
|
||||
"language": "Language",
|
||||
"font": "Terminal font",
|
||||
"more": "More options — themes, fonts, the pet — in Settings → Appearance."
|
||||
},
|
||||
"pet": {
|
||||
"title": "Pick a companion",
|
||||
"body": "A small pet sits in the corner and reacts to what your agents do. Hide it any time."
|
||||
},
|
||||
"notify": {
|
||||
"title": "Set up notifications",
|
||||
"body": "AgentHub tells you when agents are done or need you.",
|
||||
"enable": "Desktop notifications",
|
||||
"enableHint": "Your browser asks for permission once.",
|
||||
"blocked": "Notifications are blocked for this site — allow them in the browser's site settings.",
|
||||
"sound": "Notification sound",
|
||||
"preview": "Play",
|
||||
"test": "Send test notification"
|
||||
},
|
||||
"tour": {
|
||||
"progress": "Step {{n}} of {{total}}",
|
||||
"skip": "Skip tour",
|
||||
"next": "Next",
|
||||
"done": "Done",
|
||||
"goWorkspaces": "Go to Workspaces",
|
||||
"add": {
|
||||
"title": "Create your first workspace",
|
||||
"body": "A workspace is one project folder where your agents work. Click here to add one."
|
||||
},
|
||||
"folder": {
|
||||
"title": "Choose the project folder",
|
||||
"body": "Type a path or Browse to the folder of your project. A git repository gets a Changes panel too."
|
||||
},
|
||||
"create": {
|
||||
"title": "Create it",
|
||||
"body": "Pick the AI it starts on, then click Add workspace."
|
||||
},
|
||||
"prompt": {
|
||||
"title": "Start your first session",
|
||||
"body": "Tell the agent what to do and press Enter. AI, model and permission mode sit right here."
|
||||
},
|
||||
"session": {
|
||||
"title": "Your session is live",
|
||||
"body": "Watch it work and type into it like a terminal. Changes and Files are in the panel on the right; every session of this workspace is listed in the sidebar."
|
||||
},
|
||||
"finish": {
|
||||
"title": "You're all set",
|
||||
"body": "Run setup again or change any of it in Settings."
|
||||
}
|
||||
},
|
||||
"rerun": {
|
||||
"label": "First-run setup",
|
||||
"description": "Go through the welcome steps and the tour again.",
|
||||
"button": "Run setup again"
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
"bubbles": "Speech bubbles",
|
||||
"bubblesDesc": "Let the pet comment on your sessions in a bubble",
|
||||
"sizeDesc": "{{min}}–{{max}} px; the preview is capped at 120 px",
|
||||
"chooseDesc": "Featured pets come from codex-pets.net and are downloaded into this install the first time you pick one. Your choice and size are saved in this browser.",
|
||||
"chooseDesc": "Featured pets come from codex-pets.net and are downloaded into this install the first time you pick one. Your choice and size are saved to your account.",
|
||||
"uploadHint": "An image (PNG, JPEG, GIF, WebP — no SVG) is shown still; a .codex-pet.zip (pet.json + a 1536×1872 spritesheet.webp) is animated by mood.",
|
||||
"panel": {
|
||||
"label": "Sagi companion",
|
||||
@@ -189,7 +189,7 @@
|
||||
"alert": "Alert",
|
||||
"forType": "Sound for this type"
|
||||
},
|
||||
"scopeNote": "These notification settings are saved in this browser only. Alert rules and webhooks below are server-wide: they apply to everyone using this dashboard.",
|
||||
"scopeNote": "These notification settings are saved to your account. Alert rules and webhooks below are server-wide: they apply to everyone using this dashboard.",
|
||||
"insecureOrigin": "This page is served from {{origin}}, which is not a secure origin. Browsers only show notifications on HTTPS or localhost — open the dashboard through one of those to get them.",
|
||||
"unsupported": "This browser does not support notifications.",
|
||||
"deniedHelp": "Notifications are blocked for this site. To allow them again, open the site settings (the icon left of the address bar), set Notifications to Allow, then reload this page.",
|
||||
@@ -309,6 +309,7 @@
|
||||
"label": "Start AgentHub automatically",
|
||||
"desc_systemd": "Run the dashboard as a systemd --user service, so it starts at boot without Claude Code being opened. Turning it off keeps the running server up.",
|
||||
"desc_launchd": "Run the dashboard as a LaunchAgent, so it starts at login without Claude Code being opened. Turning it off keeps the running server up.",
|
||||
"desc_systemd-system": "Run the dashboard as the system service agenthub.service (server mode, one Linux user per account), so it starts with the machine.",
|
||||
"unavailable": "Not available here: needs systemd --user on Linux, or a GUI login on macOS.",
|
||||
"foreign": "A service named agenthub that AgentHub did not write already exists — AgentHub leaves it alone."
|
||||
},
|
||||
@@ -662,7 +663,12 @@
|
||||
"test": "Test",
|
||||
"testing": "Testing…",
|
||||
"testOk": "answered — {{model}} is reachable with this key.",
|
||||
"testFailed": "no answer ({{status}}): {{message}}"
|
||||
"testFailed": "no answer ({{status}}): {{message}}",
|
||||
"personal": "Personal",
|
||||
"fieldScope": "Who can use it",
|
||||
"scopeShared": "Everyone (shared)",
|
||||
"scopePersonal": "Only me (personal)",
|
||||
"fieldScopeHint": "A shared profile's key never reaches anyone's browser. A personal one is listed, edited and used by you alone."
|
||||
},
|
||||
"aiTools": {
|
||||
"title": "AI Tools",
|
||||
@@ -697,7 +703,7 @@
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Appearance",
|
||||
"description": "Colour mode and theme. Saved in this browser only.",
|
||||
"description": "Colour mode and theme. Saved to your account, so every browser you sign in to gets them.",
|
||||
"mode": "Mode",
|
||||
"modes": {
|
||||
"dark": "Dark",
|
||||
@@ -882,7 +888,7 @@
|
||||
},
|
||||
"language": {
|
||||
"title": "Language",
|
||||
"description": "The language of the whole dashboard, remembered in this browser.",
|
||||
"description": "The language of the whole dashboard, saved to your account.",
|
||||
"label": "Interface language",
|
||||
"names": {
|
||||
"en": "English",
|
||||
@@ -891,7 +897,7 @@
|
||||
},
|
||||
"terminalFont": {
|
||||
"title": "Terminal font",
|
||||
"description": "The font of the workspace terminals. Takes effect the next time a terminal opens. Saved in this browser only.",
|
||||
"description": "The font of the workspace terminals. Takes effect the next time a terminal opens. Saved to your account.",
|
||||
"label": "Font",
|
||||
"names": {
|
||||
"jetbrains": "JetBrains Mono",
|
||||
@@ -1001,7 +1007,7 @@
|
||||
},
|
||||
"prefsBackup": {
|
||||
"title": "This browser's preferences",
|
||||
"description": "Theme, language, notification settings, pet, sidebar and workspace layout are kept in this browser. Save them to a file to move them to another browser, or restore them from one.",
|
||||
"description": "Theme, language, notification settings and pet follow your account; the sidebar and workspace layout are kept in this browser. Save them to a file to move them to another browser, or restore them from one.",
|
||||
"export": "Export preferences",
|
||||
"import": "Import preferences…",
|
||||
"importTitle": "Import preferences?",
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"brand": "AgentHub",
|
||||
"progress": "{{n}} / {{total}}",
|
||||
"skip": "Bỏ qua thiết lập",
|
||||
"back": "Quay lại",
|
||||
"continue": "Tiếp tục",
|
||||
"finish": "Thêm workspace đầu tiên",
|
||||
"agent": {
|
||||
"eyebrow": "Chào mừng đến AgentHub",
|
||||
"title": "Chọn agent mặc định",
|
||||
"body": "AgentHub chạy được mọi CLI agent. Chọn cái bạn dùng nhiều nhất — đổi lúc nào cũng được.",
|
||||
"detected": "Đã có trên máy này · {{count}}",
|
||||
"none": "Chưa có AI tool nào được cài. Admin cài trong Cài đặt → AI Tools; bạn có thể hoàn tất thiết lập và chọn sau.",
|
||||
"more": "Xem thêm {{count}} agent",
|
||||
"less": "Thu gọn",
|
||||
"notInstalled": "chưa cài",
|
||||
"permission": "Session mới bắt đầu ở chế độ",
|
||||
"permissionHint": "Bypass permissions không bao giờ là mặc định: nó vẫn là công tắc đỏ bạn bật cho từng session.",
|
||||
"modes": {
|
||||
"auto": "Tự động",
|
||||
"acceptEdits": "Nhận sửa đổi",
|
||||
"default": "Hỏi trước",
|
||||
"plan": "Lập kế hoạch"
|
||||
}
|
||||
},
|
||||
"look": {
|
||||
"title": "Làm cho nó thật quen thuộc",
|
||||
"body": "Chọn giao diện bạn sẽ nhìn hàng giờ liền.",
|
||||
"modes": {
|
||||
"system": "Hệ thống",
|
||||
"dark": "Tối",
|
||||
"light": "Sáng"
|
||||
},
|
||||
"modeHints": {
|
||||
"system": "Theo hệ điều hành",
|
||||
"dark": "Dịu mắt",
|
||||
"light": "Sáng rõ"
|
||||
},
|
||||
"darkThemes": "Theme tối",
|
||||
"lightThemes": "Theme sáng",
|
||||
"language": "Ngôn ngữ",
|
||||
"font": "Font terminal",
|
||||
"more": "Thêm tuỳ chọn — theme, font, thú cưng — trong Cài đặt → Giao diện."
|
||||
},
|
||||
"pet": {
|
||||
"title": "Chọn một người bạn đồng hành",
|
||||
"body": "Một thú cưng nhỏ ngồi ở góc màn hình và phản ứng với việc agent đang làm. Ẩn lúc nào cũng được."
|
||||
},
|
||||
"notify": {
|
||||
"title": "Thiết lập thông báo",
|
||||
"body": "AgentHub báo cho bạn khi agent xong việc hoặc cần bạn.",
|
||||
"enable": "Thông báo trên máy",
|
||||
"enableHint": "Trình duyệt sẽ hỏi quyền một lần.",
|
||||
"blocked": "Trang này đang bị chặn thông báo — cho phép trong cài đặt trang của trình duyệt.",
|
||||
"sound": "Âm thanh thông báo",
|
||||
"preview": "Nghe thử",
|
||||
"test": "Gửi thông báo thử"
|
||||
},
|
||||
"tour": {
|
||||
"progress": "Bước {{n}} / {{total}}",
|
||||
"skip": "Bỏ qua hướng dẫn",
|
||||
"next": "Tiếp",
|
||||
"done": "Xong",
|
||||
"goWorkspaces": "Mở Workspaces",
|
||||
"add": {
|
||||
"title": "Tạo workspace đầu tiên",
|
||||
"body": "Workspace là một thư mục dự án nơi các agent làm việc. Bấm vào đây để thêm."
|
||||
},
|
||||
"folder": {
|
||||
"title": "Chọn thư mục dự án",
|
||||
"body": "Gõ đường dẫn hoặc bấm Browse tới thư mục dự án. Repo git sẽ có thêm bảng Changes."
|
||||
},
|
||||
"create": {
|
||||
"title": "Tạo workspace",
|
||||
"body": "Chọn AI sẽ dùng, rồi bấm Thêm workspace."
|
||||
},
|
||||
"prompt": {
|
||||
"title": "Bắt đầu session đầu tiên",
|
||||
"body": "Viết việc cần agent làm rồi nhấn Enter. Chọn AI, model và chế độ quyền ngay tại đây."
|
||||
},
|
||||
"session": {
|
||||
"title": "Session đang chạy",
|
||||
"body": "Xem agent làm việc và gõ vào như một terminal. Changes và Files nằm ở bảng bên phải; mọi session của workspace này có trong thanh bên."
|
||||
},
|
||||
"finish": {
|
||||
"title": "Xong rồi!",
|
||||
"body": "Chạy lại thiết lập hoặc đổi bất cứ lựa chọn nào trong Cài đặt."
|
||||
}
|
||||
},
|
||||
"rerun": {
|
||||
"label": "Thiết lập ban đầu",
|
||||
"description": "Đi lại các bước chào mừng và phần hướng dẫn.",
|
||||
"button": "Chạy lại thiết lập"
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
"bubbles": "Bong bóng lời thoại",
|
||||
"bubblesDesc": "Cho pet bình luận về các session của bạn trong bong bóng",
|
||||
"sizeDesc": "{{min}}–{{max}} px; bản xem trước tối đa 120 px",
|
||||
"chooseDesc": "Pet nổi bật lấy từ codex-pets.net và được tải về máy ở lần đầu bạn chọn. Lựa chọn và kích thước được lưu trong trình duyệt này.",
|
||||
"chooseDesc": "Pet nổi bật lấy từ codex-pets.net và được tải về máy ở lần đầu bạn chọn. Lựa chọn và kích thước được lưu vào tài khoản của bạn.",
|
||||
"uploadHint": "Ảnh (PNG, JPEG, GIF, WebP — không nhận SVG) hiển thị tĩnh; file .codex-pet.zip (pet.json + spritesheet.webp 1536×1872) chuyển động theo tâm trạng.",
|
||||
"panel": {
|
||||
"label": "Trợ lý Sagi",
|
||||
@@ -189,7 +189,7 @@
|
||||
"alert": "Cảnh báo",
|
||||
"forType": "Âm thanh cho loại này"
|
||||
},
|
||||
"scopeNote": "Các cài đặt thông báo này chỉ lưu trong trình duyệt này. Quy tắc cảnh báo và webhook bên dưới áp dụng cho toàn máy chủ: mọi người dùng bảng điều khiển này đều chịu ảnh hưởng.",
|
||||
"scopeNote": "Các cài đặt thông báo này được lưu vào tài khoản của bạn. Quy tắc cảnh báo và webhook bên dưới áp dụng cho toàn máy chủ: mọi người dùng bảng điều khiển này đều chịu ảnh hưởng.",
|
||||
"insecureOrigin": "Trang này được phục vụ từ {{origin}}, không phải nguồn bảo mật. Trình duyệt chỉ hiện thông báo trên HTTPS hoặc localhost — hãy mở bảng điều khiển qua một trong hai cách đó.",
|
||||
"unsupported": "Trình duyệt này không hỗ trợ thông báo.",
|
||||
"deniedHelp": "Thông báo đang bị chặn cho trang này. Để cho phép lại, mở cài đặt trang (biểu tượng bên trái thanh địa chỉ), đặt Thông báo thành Cho phép, rồi tải lại trang.",
|
||||
@@ -309,6 +309,7 @@
|
||||
"label": "Tự động khởi động AgentHub",
|
||||
"desc_systemd": "Chạy dashboard bằng systemd --user service để nó tự bật khi khởi động máy, không cần mở Claude Code. Tắt đi thì server đang chạy vẫn giữ nguyên.",
|
||||
"desc_launchd": "Chạy dashboard bằng LaunchAgent để nó tự bật khi đăng nhập, không cần mở Claude Code. Tắt đi thì server đang chạy vẫn giữ nguyên.",
|
||||
"desc_systemd-system": "Chạy dashboard bằng system service agenthub.service (chế độ server, mỗi tài khoản một Linux user) để nó tự bật cùng máy.",
|
||||
"unavailable": "Không khả dụng ở đây: cần systemd --user trên Linux, hoặc phiên đăng nhập GUI trên macOS.",
|
||||
"foreign": "Đã có một service tên agenthub không do AgentHub tạo — AgentHub không đụng vào."
|
||||
},
|
||||
@@ -662,7 +663,12 @@
|
||||
"test": "Kiểm tra",
|
||||
"testing": "Đang kiểm tra…",
|
||||
"testOk": "đã trả lời — {{model}} dùng được với key này.",
|
||||
"testFailed": "không trả lời ({{status}}): {{message}}"
|
||||
"testFailed": "không trả lời ({{status}}): {{message}}",
|
||||
"personal": "Riêng",
|
||||
"fieldScope": "Ai được dùng",
|
||||
"scopeShared": "Mọi người (dùng chung)",
|
||||
"scopePersonal": "Chỉ tôi (riêng)",
|
||||
"fieldScopeHint": "Key của profile dùng chung không bao giờ tới trình duyệt của ai. Profile riêng chỉ mình bạn thấy, sửa và dùng."
|
||||
},
|
||||
"aiTools": {
|
||||
"title": "AI Tools",
|
||||
@@ -697,7 +703,7 @@
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Giao diện",
|
||||
"description": "Chế độ màu và theme. Chỉ lưu trong trình duyệt này.",
|
||||
"description": "Chế độ màu và theme. Lưu vào tài khoản, nên mọi trình duyệt bạn đăng nhập đều dùng chúng.",
|
||||
"mode": "Chế độ",
|
||||
"modes": {
|
||||
"dark": "Tối",
|
||||
@@ -882,7 +888,7 @@
|
||||
},
|
||||
"language": {
|
||||
"title": "Ngôn ngữ",
|
||||
"description": "Ngôn ngữ của toàn bộ dashboard, được ghi nhớ trong trình duyệt này.",
|
||||
"description": "Ngôn ngữ của toàn bộ dashboard, được lưu vào tài khoản của bạn.",
|
||||
"label": "Ngôn ngữ giao diện",
|
||||
"names": {
|
||||
"en": "English",
|
||||
@@ -891,7 +897,7 @@
|
||||
},
|
||||
"terminalFont": {
|
||||
"title": "Font terminal",
|
||||
"description": "Font của các terminal trong workspace. Áp dụng từ lần mở terminal tiếp theo. Chỉ lưu trong trình duyệt này.",
|
||||
"description": "Font của các terminal trong workspace. Áp dụng từ lần mở terminal tiếp theo. Lưu vào tài khoản của bạn.",
|
||||
"label": "Font",
|
||||
"names": {
|
||||
"jetbrains": "JetBrains Mono",
|
||||
@@ -1001,7 +1007,7 @@
|
||||
},
|
||||
"prefsBackup": {
|
||||
"title": "Tùy chọn của trình duyệt này",
|
||||
"description": "Giao diện, ngôn ngữ, cài đặt thông báo, thú cưng, thanh bên và bố cục không gian làm việc được lưu trong trình duyệt này. Lưu chúng ra tệp để chuyển sang trình duyệt khác, hoặc khôi phục từ tệp.",
|
||||
"description": "Giao diện, ngôn ngữ, cài đặt thông báo và thú cưng theo tài khoản của bạn; thanh bên và bố cục không gian làm việc được lưu trong trình duyệt này. Lưu chúng ra tệp để chuyển sang trình duyệt khác, hoặc khôi phục từ tệp.",
|
||||
"export": "Xuất tùy chọn",
|
||||
"import": "Nhập tùy chọn…",
|
||||
"importTitle": "Nhập tùy chọn?",
|
||||
|
||||
Reference in New Issue
Block a user