diff --git a/apps/docs/app/[lang]/blog/[[...slug]]/page.tsx b/apps/docs/app/[lang]/blog/[[...slug]]/page.tsx index b706534..d1be190 100644 --- a/apps/docs/app/[lang]/blog/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/blog/[[...slug]]/page.tsx @@ -18,6 +18,21 @@ interface BlogPostData { const components = getMDXComponents() as any; +// Locale-aware UI strings for the blog chrome. Post bodies are translated +// via per-locale MDX files; these are the surrounding labels. +const BLOG_UI: Record< + string, + { title: string; subtitle: string; by: string; back: string; empty: string; dateLocale: string } +> = { + en: { title: 'Blog', subtitle: 'Insights, updates, and best practices from the ObjectStack team.', by: 'By', back: 'Back to Blog', empty: 'No blog posts yet. Check back soon!', dateLocale: 'en-US' }, + 'zh-Hans': { title: '博客', subtitle: '来自 ObjectStack 团队的洞见、更新与最佳实践。', by: '作者', back: '返回博客', empty: '还没有博客文章,敬请期待!', dateLocale: 'zh-CN' }, + ja: { title: 'ブログ', subtitle: 'ObjectStack チームによる知見、アップデート、ベストプラクティス。', by: '著者', back: 'ブログに戻る', empty: 'まだ記事がありません。近日公開予定です!', dateLocale: 'ja-JP' }, + de: { title: 'Blog', subtitle: 'Einblicke, Neuigkeiten und Best Practices vom ObjectStack-Team.', by: 'Von', back: 'Zurück zum Blog', empty: 'Noch keine Beiträge. Schau bald wieder vorbei!', dateLocale: 'de-DE' }, + es: { title: 'Blog', subtitle: 'Ideas, novedades y buenas prácticas del equipo de ObjectStack.', by: 'Por', back: 'Volver al blog', empty: 'Aún no hay publicaciones. ¡Vuelve pronto!', dateLocale: 'es-ES' }, + fr: { title: 'Blog', subtitle: 'Analyses, actualités et bonnes pratiques de l’équipe ObjectStack.', by: 'Par', back: 'Retour au blog', empty: 'Pas encore d’articles. Revenez bientôt !', dateLocale: 'fr-FR' }, + ko: { title: '블로그', subtitle: 'ObjectStack 팀의 인사이트, 업데이트, 모범 사례.', by: '작성자', back: '블로그로 돌아가기', empty: '아직 게시글이 없습니다. 곧 다시 확인해 주세요!', dateLocale: 'ko-KR' }, +}; + export default async function BlogPage({ params, }: { @@ -25,17 +40,19 @@ export default async function BlogPage({ }) { const { slug, lang } = await params; + const ui = BLOG_UI[lang] ?? BLOG_UI.en; + // If no slug, show blog index if (!slug || slug.length === 0) { - const posts = blog.getPages(); + const posts = blog.getPages(lang); return (
-

Blog

+

{ui.title}

- Insights, updates, and best practices from the ObjectStack team. + {ui.subtitle}

@@ -62,7 +79,7 @@ export default async function BlogPage({
{postData.date && ( )} {postData.author && ( - By {postData.author} + {ui.by} {postData.author} )}
@@ -93,7 +110,7 @@ export default async function BlogPage({ {posts.length === 0 && (
-

No blog posts yet. Check back soon!

+

{ui.empty}

)}
@@ -102,8 +119,8 @@ export default async function BlogPage({ } // Show individual blog post - const page = blog.getPage(slug); - + const page = blog.getPage(slug, lang); + if (!page) { notFound(); } @@ -114,12 +131,12 @@ export default async function BlogPage({ return (
- - Back to Blog + {ui.back}
@@ -135,7 +152,7 @@ export default async function BlogPage({
{pageData.date && ( )} {pageData.author && ( - By {pageData.author} + {ui.by} {pageData.author} )}
@@ -168,19 +185,17 @@ export default async function BlogPage({ ); } -export async function generateStaticParams() { - return blog.getPages().map((page) => ({ - slug: page.slugs, - })); +export function generateStaticParams() { + return blog.generateParams(); } export async function generateMetadata({ params, }: { - params: Promise<{ slug?: string[] }>; + params: Promise<{ lang: string; slug?: string[] }>; }) { - const { slug } = await params; - + const { slug, lang } = await params; + // If no slug, return default metadata for blog index if (!slug || slug.length === 0) { return { @@ -188,8 +203,8 @@ export async function generateMetadata({ description: 'Insights, updates, and best practices from the ObjectStack team.', }; } - - const page = blog.getPage(slug); + + const page = blog.getPage(slug, lang); if (!page) { notFound(); diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx index 391a282..26cb24c 100644 --- a/apps/docs/app/[lang]/layout.tsx +++ b/apps/docs/app/[lang]/layout.tsx @@ -10,6 +10,7 @@ const LANGUAGE_NAMES: Record = { de: 'Deutsch', es: 'Español', fr: 'Français', + ko: '한국어', }; export default async function LanguageLayout({ diff --git a/apps/docs/app/[lang]/page.tsx b/apps/docs/app/[lang]/page.tsx index 00ec753..a24d5b8 100644 --- a/apps/docs/app/[lang]/page.tsx +++ b/apps/docs/app/[lang]/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { Server, Container, ShieldOff, KeyRound, Lock, LineChart, Wrench, ShieldCheck, Briefcase, Ship, Boxes, WifiOff, ArrowRight } from 'lucide-react'; +import { Server, Container, ShieldOff, KeyRound, Lock, LineChart, Wrench, ShieldCheck, Briefcase, Ship, Boxes, WifiOff, ArrowRight, Database } from 'lucide-react'; import { HomeLayout } from 'fumadocs-ui/layouts/home'; import { baseOptions } from '@/lib/layout.shared'; import { getHomepageTranslations } from '@/lib/homepage-i18n'; @@ -36,6 +36,13 @@ export default async function HomePage({ ]; const features = [ + { + key: 'connectSystems', + icon: Database, + href: '/docs/configure/data-sources', + title: t.features.connectSystems.title, + description: t.features.connectSystems.description, + }, { key: 'selfHosted', icon: Server, diff --git a/apps/docs/lib/homepage-i18n.ts b/apps/docs/lib/homepage-i18n.ts index fab7000..e027a8a 100644 --- a/apps/docs/lib/homepage-i18n.ts +++ b/apps/docs/lib/homepage-i18n.ts @@ -48,6 +48,10 @@ export interface HomepageTranslations { // Features Section features: { + connectSystems: { + title: string; + description: string; + }; selfHosted: { title: string; description: string; @@ -215,6 +219,10 @@ export const en: HomepageTranslations = { }, }, features: { + connectSystems: { + title: 'Extend the Systems You Already Run', + description: 'Connect ObjectOS to your existing business databases, model the tables as objects, and add AI-native query, analysis, and automation on top — without migrating off your system of record. A coding agent can scan the schema and generate the objects for you.', + }, selfHosted: { title: 'Every Object Is an AI Tool', description: 'Define a business object once in ObjectStack metadata, and ObjectOS automatically exposes it to AI agents as a governed, callable tool — no glue code, no separate integration layer to maintain.', @@ -370,6 +378,10 @@ export const zhHans: HomepageTranslations = { }, }, features: { + connectSystems: { + title: '直接扩展你现有的业务系统', + description: '把 ObjectOS 接到你现有的业务数据库,用对象描述这些表,就能在不迁移、不动原系统的前提下,叠加 AI 原生的查询、分析与自动化能力。还能让编码 Agent 扫描库表、自动生成这些对象。', + }, selfHosted: { title: '每个业务对象,自动就是 AI 工具', description: '在 ObjectStack 里把业务对象定义一次,ObjectOS 就会自动把它暴露成 AI Agent 可调用的工具 —— 不需要再写胶水代码,也不需要单独维护一层 AI 集成。', @@ -487,6 +499,801 @@ export const zhHans: HomepageTranslations = { }, }; +/** + * Japanese (日本語) Translations + */ +export const ja: HomepageTranslations = { + badge: { + status: 'AI ネイティブのビジネスプラットフォーム', + version: SPEC_VERSION, + }, + hero: { + title: { + line1: 'AI が本当に使える', + line2: 'ビジネスプラットフォーム。', + }, + subtitle: { + line1: 'ObjectOS は、あなたのビジネスシステムのための AI ネイティブなランタイムです —— CRM、契約、チケット、承認など、ObjectStack メタデータプロトコルでモデル化したあらゆるものが対象です。', + line2: 'ビジネスオブジェクトを ObjectOS に載せれば、AI エージェントはそのデータを安全に照会・分析し、操作できます —— あなたの権限のもとで、あなたのサーバー上で、すべての手順を監査しながら。', + }, + cta: { + primary: '仕組みを見る', + primaryHref: '/docs/architecture', + secondary: 'クイックスタート', + secondaryHref: '/docs/quickstart', + }, + quickStart: { + label: 'ターミナル', + commands: [ + 'npm i -g @objectstack/cli', + 'os start', + ], + }, + trustStrip: { + license: 'MIT ライセンス', + selfHosted: 'セルフホスト', + version: OBJECTOS_VERSION, + runtime: 'Docker · Kubernetes · エアギャップ', + }, + }, + features: { + connectSystems: { + title: 'すでに運用しているシステムを拡張', + description: 'ObjectOS を既存のビジネスデータベースに接続し、テーブルをオブジェクトとしてモデル化すれば、システム・オブ・レコードから移行することなく、AI ネイティブな照会・分析・自動化をその上に追加できます。コーディングエージェントがスキーマをスキャンし、オブジェクトを自動生成することもできます。', + }, + selfHosted: { + title: 'すべてのオブジェクトが AI ツールに', + description: 'ビジネスオブジェクトを ObjectStack メタデータで一度定義すれば、ObjectOS が自動的にそれをガバナンスの効いた呼び出し可能なツールとして AI エージェントに公開します —— 接着コードも、個別に保守する統合レイヤーも不要です。', + }, + deployAnywhere: { + title: 'AI はサインインしたユーザーとして動く', + description: 'エージェントがデータを照会・更新するとき、それは呼び出し元の本人として実行されます。その人が見られるもの、できることだけをエージェントもでき、それ以上はありません。この境界はプロンプトではなく、ランタイムで強制されます。', + }, + airGapped: { + title: 'すべての操作を監査', + description: 'すべての読み取り、すべての書き込み、すべてのエスカレーション —— 人によるものでもエージェントによるものでも —— 誰が、何を、いつ、なぜ行ったかが記録されます。コンプライアンスが確認するログは、2 つではなく 1 つです。', + }, + identity: { + title: 'あなたの ID 基盤に接続', + description: 'OAuth、OIDC、SAML、社内 SSO、またはローカルアカウント。AI セッションは同じ ID、同じ MFA、同じオフボーディングを引き継ぎます —— 個別に統制すべき「AI アカウント」は存在しません。', + }, + permissions: { + title: '権限はランタイムで強制', + description: 'ロールベースのアクセス、レコード単位のルール、フィールド単位のマスキングが ObjectOS 内部で実行されます —— だからリクエストが UI、API クライアント、AI エージェントのどこから来ても、同じポリシーが適用されます。', + }, + observability: { + title: 'あなたのデータ、あなたのネットワーク', + description: 'ObjectOS はあなたの環境で動作します —— プライベートクラウド、オンプレミス、または完全なエアギャップ環境。ビジネスデータも AI プロンプトも、あなたの境界の内側にとどまります。途中に第三者は入りません。', + }, + }, + capabilities: { + eyebrow: '02 — 安全である理由', + heading: 'コントロールを手放さずに AI アクセスを', + subheading: 'あなたのプラットフォームがすでに備えるすべての安全策 —— ID、権限、監査、ネットワーク境界 —— が、エージェントがデータに触れた瞬間に適用されます。新たに統制すべきものはありません。', + }, + personas: { + eyebrow: '03 — 対象となる人', + heading: '「ゴーサイン」を出す 3 者のために設計', + itOps: { + title: 'IT・プラットフォーム運用', + description: 'Docker イメージと Helm チャートとして提供されます。あなたがすでに運用しているデータベース、ID、可観測性スタックに組み込めます。1 つの成果物が、ノート PC からエアギャップのデータセンターまで同じように動作します。', + action: 'デプロイガイド', + }, + security: { + title: 'セキュリティ・コンプライアンス', + description: 'AI エージェントは、その背後にいるユーザー以上のものを決して見たり実行したりできません。SSO、RBAC、フィールド単位のマスキング、完全な監査はランタイムで強制され —— 人と AI のトラフィックに同一に適用されます。', + action: '権限モデル', + }, + business: { + title: 'ビジネスオーナー', + description: 'あなたの CRM、契約、チケットを本当に理解した AI をチームに —— コピーではなく、システム・オブ・レコードそのものと対話するからです。顧客データが会社の外に出ることはありません。', + action: 'アーキテクチャ概要', + }, + }, + deployModes: { + eyebrow: '01 — デプロイ', + heading: 'データがすでにある場所で動く', + subheading: '同じ成果物、同じ動作。あなたのインフラに合った形態を選んでください —— ObjectOS と、それがもたらす AI アクセスは、あなたのネットワーク内にとどまります。', + docker: { + title: 'Docker', + tagline: '単一ホスト、単一コマンド', + description: '1 つのコンテナをあなた自身のデータベースに接続。評価、社内ツール、小規模チームでの導入に最適です。', + href: '/docs/deploy/docker', + cta: 'Docker ガイド', + }, + kubernetes: { + title: 'Kubernetes', + tagline: '本番 HA', + description: 'Deployment + Service + PersistentVolumeClaim。シークレットと設定は、あなたがすでに使っている仕組みから取り込めます。Helm チャートを同梱。', + href: '/docs/deploy/kubernetes', + cta: 'Kubernetes ガイド', + }, + airGapped: { + title: 'エアギャップ', + tagline: 'インターネット送信なし', + description: 'リリースバンドルを公衆接続のないネットワークへ持ち込みます。ObjectOS は成果物をディスクから読み込み、外部へ通信することはありません —— AI 連携面も同様です。', + href: '/docs/deploy/air-gapped', + cta: 'エアギャップガイド', + }, + }, + bottomCta: { + heading: 'AI が本当に使えるビジネスシステムを。', + subheading: 'ObjectStack でオブジェクトを一度定義し、ObjectOS で動かす。AI に働いてもらいましょう —— あなたのルールのもとで、あなたのネットワークの内側で。', + primary: 'クイックスタート', + primaryHref: '/docs/quickstart', + secondary: 'アーキテクチャ', + secondaryHref: '/docs/architecture', + }, + footer: { + copyright: '© 2026 ObjectStack AI LLC.', + privacy: 'プライバシーポリシー', + privacyHref: '/privacy', + terms: '利用規約', + termsHref: '/terms', + }, + agentPreview: { + windowTitle: 'Console · サポートコパイロット', + userLabel: 'あなた', + agentLabel: 'Agent', + user1Line1: '24 時間以上オープンのままの緊急チケットはどれで、', + user1Line2: '誰が担当している?', + tool1Name: 'query_data', + tool1Target: ' · support_ticket', + matchSummary: ' 3 件が該当 —— 担当者別にグループ化:', + ticket1Meta: ' · 31h オープン · Maya ', + ticket1Note: '(請求障害)', + ticket2Meta: ' · 28h オープン · Maya ', + ticket2Note: '(SSO 障害)', + ticket3Meta: ' · 26h オープン · Jordan', + ticket3Note: ' (エクスポート不具合)', + insight: ' Maya が 2/3 を抱えています —— ボトルネックの可能性大。', + user2: '3 件すべてをエスカレーションし、Maya の分はオンコール担当に再割り当てして。', + tool2Name: 'action_escalate_ticket', + tool2Count: ' ×3 · ', + tool2Sep: '', + tool3Name: 'action_reassign', + tool3Count: ' ×2', + actionResult: ' ✓ 優先度 → 緊急 · オンコールに通知 · 監査済み', + governance: ' あなた本人として実行。権限とフィールドルールが適用されます。', + }, +}; + +/** + * German (Deutsch) Translations + */ +export const de: HomepageTranslations = { + badge: { + status: 'KI-native Business-Plattform', + version: SPEC_VERSION, + }, + hero: { + title: { + line1: 'Die Business-Plattform,', + line2: 'die KI wirklich nutzen kann.', + }, + subtitle: { + line1: 'ObjectOS ist die KI-native Runtime für deine Business-Systeme — CRM, Verträge, Tickets, Freigaben, alles, was über das ObjectStack-Metadatenprotokoll modelliert ist.', + line2: 'Lege deine Business-Objekte auf ObjectOS, und KI-Agenten können diese Daten sicher abfragen, analysieren und darauf handeln — unter deinen Berechtigungen, auf deinen Servern, mit jedem Schritt im Audit.', + }, + cta: { + primary: 'So funktioniert es', + primaryHref: '/docs/architecture', + secondary: 'Quickstart', + secondaryHref: '/docs/quickstart', + }, + quickStart: { + label: 'Terminal', + commands: [ + 'npm i -g @objectstack/cli', + 'os start', + ], + }, + trustStrip: { + license: 'MIT-lizenziert', + selfHosted: 'Self-hosted', + version: OBJECTOS_VERSION, + runtime: 'Docker · Kubernetes · Air-Gapped', + }, + }, + features: { + connectSystems: { + title: 'Erweitere die Systeme, die du schon betreibst', + description: 'Verbinde ObjectOS mit deinen bestehenden Business-Datenbanken, modelliere die Tabellen als Objekte und ergänze KI-native Abfrage, Analyse und Automatisierung obendrauf — ohne dein System of Record zu migrieren. Ein Coding-Agent kann das Schema scannen und die Objekte für dich generieren.', + }, + selfHosted: { + title: 'Jedes Objekt ist ein KI-Tool', + description: 'Definiere ein Business-Objekt einmal in ObjectStack-Metadaten, und ObjectOS stellt es KI-Agenten automatisch als kontrolliertes, aufrufbares Tool bereit — kein Glue-Code, keine separate Integrationsschicht, die du pflegen musst.', + }, + deployAnywhere: { + title: 'KI handelt als der angemeldete Nutzer', + description: 'Wenn ein Agent Daten abfragt oder aktualisiert, läuft er mit der Identität des Aufrufers. Was diese Person sehen oder tun darf, darf der Agent — nicht mehr. Die Grenze wird in der Runtime durchgesetzt, nicht im Prompt.', + }, + airGapped: { + title: 'Jede Aktion auditiert', + description: 'Jeder Lesezugriff, jeder Schreibzugriff, jede Eskalation — durch einen Menschen oder einen Agenten — wird mit Wer, Was, Wann und Warum festgehalten. Compliance hat ein Log zu prüfen, nicht zwei.', + }, + identity: { + title: 'Bindet sich an deine Identität an', + description: 'OAuth, OIDC, SAML, Unternehmens-SSO oder lokale Konten. KI-Sessions erben dieselbe Identität, dasselbe MFA, denselben Offboarding-Prozess — es gibt kein separates „KI-Konto“, das verwaltet werden muss.', + }, + permissions: { + title: 'Berechtigungen in der Runtime durchgesetzt', + description: 'Rollenbasierter Zugriff, Regeln auf Datensatzebene und Redaktion auf Feldebene laufen innerhalb von ObjectOS — sodass dieselbe Policy gilt, egal ob eine Anfrage aus der UI, von einem API-Client oder von einem KI-Agenten kommt.', + }, + observability: { + title: 'Deine Daten, dein Netzwerk', + description: 'ObjectOS läuft in deiner Umgebung — Private Cloud, On-Prem oder vollständig Air-Gapped. Business-Daten und KI-Prompts bleiben innerhalb deines Perimeters. Kein Dritter im Spiel.', + }, + }, + capabilities: { + eyebrow: '02 — Wie es sicher ist', + heading: 'KI-Zugriff, ohne die Kontrolle abzugeben', + subheading: 'Jede Schutzmaßnahme, die deine Plattform bereits hat — Identität, Berechtigungen, Audit, Netzwerkgrenze — greift in dem Moment, in dem ein Agent deine Daten berührt. Nichts Neues zu verwalten.', + }, + personas: { + eyebrow: '03 — Für wen es ist', + heading: 'Gebaut für die drei Menschen, die Ja sagen müssen', + itOps: { + title: 'IT & Plattform-Betrieb', + description: 'Wird als Docker-Image und Helm-Chart ausgeliefert. Bindet sich an die Datenbank, Identität und den Observability-Stack an, die du bereits betreibst. Ein Artefakt läuft gleich — vom Laptop bis zum Air-Gapped-Rechenzentrum.', + action: 'Deployment-Leitfaden', + }, + security: { + title: 'Security & Compliance', + description: 'KI-Agenten können nie mehr sehen oder tun als der Nutzer hinter ihnen. SSO, RBAC, Redaktion auf Feldebene und vollständiges Audit werden in der Runtime durchgesetzt — und gelten identisch für menschlichen und KI-Traffic.', + action: 'Berechtigungsmodell', + }, + business: { + title: 'Business-Verantwortliche', + description: 'Gib deinen Teams eine KI, die dein CRM, deine Verträge, deine Tickets wirklich kennt — weil sie mit dem System of Record spricht, nicht mit einer Kopie. Kundendaten verlassen dein Unternehmen nie.', + action: 'Architektur-Überblick', + }, + }, + deployModes: { + eyebrow: '01 — Deploy', + heading: 'Läuft, wo deine Daten ohnehin liegen', + subheading: 'Dasselbe Artefakt, dasselbe Verhalten. Wähle die Form, die zu deiner Infrastruktur passt — ObjectOS und der KI-Zugriff, der dazugehört, bleiben in deinem Netzwerk.', + docker: { + title: 'Docker', + tagline: 'Ein Host, ein Befehl', + description: 'Ein Container gegen deine eigene Datenbank. Ideal für Evaluierung, interne Tools und Deployments für kleine Teams.', + href: '/docs/deploy/docker', + cta: 'Docker-Leitfaden', + }, + kubernetes: { + title: 'Kubernetes', + tagline: 'Produktions-HA', + description: 'Deployment + Service + PersistentVolumeClaim, mit Secrets und Config aus den Mechanismen, die du bereits nutzt. Helm-Chart inklusive.', + href: '/docs/deploy/kubernetes', + cta: 'Kubernetes-Leitfaden', + }, + airGapped: { + title: 'Air-Gapped', + tagline: 'Kein Internet-Egress', + description: 'Liefere ein Release-Bundle in ein Netzwerk ohne öffentliche Konnektivität. ObjectOS liest sein Artefakt von der Festplatte und ruft nie nach Hause — einschließlich der KI-Integrationsfläche.', + href: '/docs/deploy/air-gapped', + cta: 'Air-Gapped-Leitfaden', + }, + }, + bottomCta: { + heading: 'Gib der KI ein Business-System, das sie wirklich nutzen kann.', + subheading: 'Definiere deine Objekte einmal mit ObjectStack. Betreibe sie auf ObjectOS. Lass die KI arbeiten — nach deinen Regeln, in deinem Netzwerk.', + primary: 'Quickstart', + primaryHref: '/docs/quickstart', + secondary: 'Architektur', + secondaryHref: '/docs/architecture', + }, + footer: { + copyright: '© 2026 ObjectStack AI LLC.', + privacy: 'Datenschutzerklärung', + privacyHref: '/privacy', + terms: 'Nutzungsbedingungen', + termsHref: '/terms', + }, + agentPreview: { + windowTitle: 'Console · Support-Copilot', + userLabel: 'Du', + agentLabel: 'Agent', + user1Line1: 'Welche dringenden Tickets sind seit über 24 h offen,', + user1Line2: 'und wer ist verantwortlich?', + tool1Name: 'query_data', + tool1Target: ' · support_ticket', + matchSummary: ' 3 Tickets passen — gruppiert nach Bearbeiter:', + ticket1Meta: ' · 31h offen · Maya ', + ticket1Note: '(Abrechnungsausfall)', + ticket2Meta: ' · 28h offen · Maya ', + ticket2Note: '(SSO-Ausfall)', + ticket3Meta: ' · 26h offen · Jordan', + ticket3Note: ' (Export-Bug)', + insight: ' Maya trägt 2/3 — wahrscheinlich der Engpass.', + user2: 'Eskaliere alle drei und weise Mayas dem Bereitschaftsdienst zu.', + tool2Name: 'action_escalate_ticket', + tool2Count: ' ×3 · ', + tool2Sep: '', + tool3Name: 'action_reassign', + tool3Count: ' ×2', + actionResult: ' ✓ Priorität → dringend · Bereitschaft benachrichtigt · auditiert', + governance: ' Läuft als du. Berechtigungen + Feldregeln durchgesetzt.', + }, +}; + +/** + * Spanish (Español) Translations + */ +export const es: HomepageTranslations = { + badge: { + status: 'Plataforma de negocio AI-native', + version: SPEC_VERSION, + }, + hero: { + title: { + line1: 'La plataforma de negocio', + line2: 'que la IA sí puede usar.', + }, + subtitle: { + line1: 'ObjectOS es el runtime AI-native para tus sistemas de negocio — CRM, contratos, tickets, aprobaciones, cualquier cosa modelada con el protocolo de metadatos de ObjectStack.', + line2: 'Pon tus objetos de negocio en ObjectOS y los agentes de IA podrán consultar, analizar y actuar sobre esos datos de forma segura — bajo tus permisos, en tus servidores, con cada paso auditado.', + }, + cta: { + primary: 'Mira cómo funciona', + primaryHref: '/docs/architecture', + secondary: 'Inicio rápido', + secondaryHref: '/docs/quickstart', + }, + quickStart: { + label: 'Terminal', + commands: [ + 'npm i -g @objectstack/cli', + 'os start', + ], + }, + trustStrip: { + license: 'Licencia MIT', + selfHosted: 'Autoalojado', + version: OBJECTOS_VERSION, + runtime: 'Docker · Kubernetes · Air-gapped', + }, + }, + features: { + connectSystems: { + title: 'Extiende los sistemas que ya operas', + description: 'Conecta ObjectOS a tus bases de datos de negocio existentes, modela las tablas como objetos y añade encima consulta, análisis y automatización AI-native — sin migrar fuera de tu sistema de registro. Un agente de programación puede escanear el esquema y generar los objetos por ti.', + }, + selfHosted: { + title: 'Cada objeto es una herramienta de IA', + description: 'Define un objeto de negocio una sola vez en los metadatos de ObjectStack, y ObjectOS lo expone automáticamente a los agentes de IA como una herramienta gobernada e invocable — sin código de pegamento, sin una capa de integración aparte que mantener.', + }, + deployAnywhere: { + title: 'La IA actúa como el usuario autenticado', + description: 'Cuando un agente consulta o actualiza datos, se ejecuta con la identidad de quien lo invoca. Lo que esa persona puede ver o hacer, el agente puede hacerlo — nada más. El límite se impone en el runtime, no en el prompt.', + }, + airGapped: { + title: 'Cada acción auditada', + description: 'Cada lectura, cada escritura, cada escalación — por una persona o un agente — queda registrada con el quién, qué, cuándo y por qué. Cumplimiento tiene un solo registro que revisar, no dos.', + }, + identity: { + title: 'Se integra con tu identidad', + description: 'OAuth, OIDC, SAML, SSO corporativo o cuentas locales. Las sesiones de IA heredan la misma identidad, el mismo MFA, el mismo proceso de baja — no hay una "cuenta de IA" aparte que gobernar.', + }, + permissions: { + title: 'Permisos impuestos en el runtime', + description: 'El acceso basado en roles, las reglas a nivel de registro y la ocultación a nivel de campo se ejecutan dentro de ObjectOS — así que la misma política aplica venga la petición de la UI, de un cliente de API o de un agente de IA.', + }, + observability: { + title: 'Tus datos, tu red', + description: 'ObjectOS se ejecuta en tu entorno — nube privada, on-premise o completamente air-gapped. Los datos de negocio y los prompts de IA permanecen dentro de tu perímetro. Ningún tercero en el medio.', + }, + }, + capabilities: { + eyebrow: '02 — Por qué es seguro', + heading: 'Acceso de IA, sin renunciar al control', + subheading: 'Cada salvaguarda que tu plataforma ya tiene — identidad, permisos, auditoría, límite de red — aplica desde el momento en que un agente toca tus datos. Nada nuevo que gobernar.', + }, + personas: { + eyebrow: '03 — Para quién es', + heading: 'Diseñado para las tres personas que tienen que decir que sí', + itOps: { + title: 'IT y operadores de plataforma', + description: 'Se entrega como una imagen de Docker y un Helm chart. Se integra con la base de datos, la identidad y el stack de observabilidad que ya operas. Un solo artefacto se ejecuta igual desde un portátil hasta un centro de datos air-gapped.', + action: 'Guía de despliegue', + }, + security: { + title: 'Seguridad y cumplimiento', + description: 'Los agentes de IA nunca pueden ver ni hacer más que el usuario detrás de ellos. SSO, RBAC, ocultación a nivel de campo y auditoría completa se imponen en el runtime — y aplican de forma idéntica al tráfico humano y al de IA.', + action: 'Modelo de permisos', + }, + business: { + title: 'Responsables de negocio', + description: 'Dale a tus equipos una IA que realmente conoce tu CRM, tus contratos, tus tickets — porque habla con el sistema de registro, no con una copia. Los datos de los clientes nunca salen de tu empresa.', + action: 'Visión de la arquitectura', + }, + }, + deployModes: { + eyebrow: '01 — Despliega', + heading: 'Se ejecuta donde tus datos ya viven', + subheading: 'Mismo artefacto, mismo comportamiento. Elige la forma que encaje con tu infraestructura — ObjectOS, y el acceso de IA que trae consigo, se quedan dentro de tu red.', + docker: { + title: 'Docker', + tagline: 'Un host, un comando', + description: 'Un contenedor contra tu propia base de datos. Ideal para evaluación, herramientas internas y despliegues de equipos pequeños.', + href: '/docs/deploy/docker', + cta: 'Guía de Docker', + }, + kubernetes: { + title: 'Kubernetes', + tagline: 'Alta disponibilidad en producción', + description: 'Deployment + Service + PersistentVolumeClaim, con secretos y configuración desde los mecanismos que ya usas. Helm chart incluido.', + href: '/docs/deploy/kubernetes', + cta: 'Guía de Kubernetes', + }, + airGapped: { + title: 'Air-Gapped', + tagline: 'Sin salida a internet', + description: 'Lleva un paquete de release a una red sin conectividad pública. ObjectOS lee su artefacto desde disco y nunca llama a casa — incluida la superficie de integración con IA.', + href: '/docs/deploy/air-gapped', + cta: 'Guía air-gapped', + }, + }, + bottomCta: { + heading: 'Dale a la IA un sistema de negocio que sí pueda usar.', + subheading: 'Define tus objetos una sola vez con ObjectStack. Ejecútalos en ObjectOS. Deja que la IA trabaje — bajo tus reglas, dentro de tu red.', + primary: 'Inicio rápido', + primaryHref: '/docs/quickstart', + secondary: 'Arquitectura', + secondaryHref: '/docs/architecture', + }, + footer: { + copyright: '© 2026 ObjectStack AI LLC.', + privacy: 'Política de privacidad', + privacyHref: '/privacy', + terms: 'Términos del servicio', + termsHref: '/terms', + }, + agentPreview: { + windowTitle: 'Console · Copiloto de soporte', + userLabel: 'Tú', + agentLabel: 'Agente', + user1Line1: 'Qué tickets urgentes llevan abiertos más de 24h,', + user1Line2: '¿y quién es el responsable?', + tool1Name: 'query_data', + tool1Target: ' · support_ticket', + matchSummary: ' 3 tickets coinciden — agrupados por responsable:', + ticket1Meta: ' · 31h abierto · Maya ', + ticket1Note: '(corte de facturación)', + ticket2Meta: ' · 28h abierto · Maya ', + ticket2Note: '(fallo de SSO)', + ticket3Meta: ' · 26h abierto · Jordan', + ticket3Note: ' (bug de exportación)', + insight: ' Maya carga con 2/3 — probablemente el cuello de botella.', + user2: 'Escala los tres y reasigna los de Maya a quien esté de guardia.', + tool2Name: 'action_escalate_ticket', + tool2Count: ' ×3 · ', + tool2Sep: '', + tool3Name: 'action_reassign', + tool3Count: ' ×2', + actionResult: ' ✓ prioridad → urgente · guardia notificada · auditado', + governance: ' Se ejecuta como tú. Permisos + reglas de campo impuestos.', + }, +}; + +/** + * French (Français) Translations + */ +export const fr: HomepageTranslations = { + badge: { + status: 'Plateforme métier AI-native', + version: SPEC_VERSION, + }, + hero: { + title: { + line1: 'La plateforme métier', + line2: 'que l’IA peut vraiment utiliser.', + }, + subtitle: { + line1: 'ObjectOS est le runtime AI-native de vos systèmes métier — CRM, contrats, tickets, approbations, tout ce qui est modélisé sur le protocole de métadonnées ObjectStack.', + line2: 'Placez vos objets métier sur ObjectOS et les agents IA peuvent interroger, analyser et agir sur ces données en toute sécurité — sous vos permissions, sur vos serveurs, chaque étape étant auditée.', + }, + cta: { + primary: 'Voir comment ça marche', + primaryHref: '/docs/architecture', + secondary: 'Démarrage rapide', + secondaryHref: '/docs/quickstart', + }, + quickStart: { + label: 'Terminal', + commands: [ + 'npm i -g @objectstack/cli', + 'os start', + ], + }, + trustStrip: { + license: 'Sous licence MIT', + selfHosted: 'Auto-hébergé', + version: OBJECTOS_VERSION, + runtime: 'Docker · Kubernetes · Air-gapped', + }, + }, + features: { + connectSystems: { + title: 'Étendez les systèmes que vous exploitez déjà', + description: 'Connectez ObjectOS à vos bases de données métier existantes, modélisez les tables sous forme d’objets et ajoutez par-dessus des capacités AI-native de requête, d’analyse et d’automatisation — sans migrer hors de votre système de référence. Un agent de code peut scanner le schéma et générer les objets pour vous.', + }, + selfHosted: { + title: 'Chaque objet est un outil pour l’IA', + description: 'Définissez un objet métier une seule fois dans les métadonnées ObjectStack, et ObjectOS l’expose automatiquement aux agents IA comme un outil gouverné et appelable — sans code de liaison, sans couche d’intégration séparée à maintenir.', + }, + deployAnywhere: { + title: 'L’IA agit en tant qu’utilisateur connecté', + description: 'Lorsqu’un agent interroge ou met à jour des données, il s’exécute avec l’identité de l’appelant. Tout ce que cette personne est autorisée à voir ou à faire, l’agent le peut — rien de plus. La frontière est appliquée dans le runtime, pas dans le prompt.', + }, + airGapped: { + title: 'Chaque action auditée', + description: 'Chaque lecture, chaque écriture, chaque escalade — par un humain ou un agent — est enregistrée avec le qui, le quoi, le quand et le pourquoi. La conformité n’a qu’un seul journal à consulter, pas deux.', + }, + identity: { + title: 'S’intègre à votre identité', + description: 'OAuth, OIDC, SAML, SSO d’entreprise ou comptes locaux. Les sessions IA héritent de la même identité, du même MFA, du même offboarding — il n’y a pas de « compte IA » distinct à gouverner.', + }, + permissions: { + title: 'Permissions appliquées dans le runtime', + description: 'L’accès basé sur les rôles, les règles au niveau des enregistrements et le masquage au niveau des champs s’exécutent à l’intérieur d’ObjectOS — la même politique s’applique donc que la requête provienne de l’UI, d’un client API ou d’un agent IA.', + }, + observability: { + title: 'Vos données, votre réseau', + description: 'ObjectOS s’exécute dans votre environnement — cloud privé, on-prem ou entièrement air-gapped. Les données métier et les prompts IA restent à l’intérieur de votre périmètre. Aucun tiers dans la boucle.', + }, + }, + capabilities: { + eyebrow: '02 — La sécurité', + heading: 'L’accès de l’IA, sans renoncer au contrôle', + subheading: 'Chaque garde-fou que votre plateforme possède déjà — identité, permissions, audit, frontière réseau — s’applique dès l’instant où un agent touche vos données. Rien de nouveau à gouverner.', + }, + personas: { + eyebrow: '03 — À qui ça s’adresse', + heading: 'Conçu pour les trois personnes qui doivent dire oui', + itOps: { + title: 'IT & exploitants de plateforme', + description: 'Livré sous forme d’image Docker et de chart Helm. S’intègre à la base de données, à l’identité et à la pile d’observabilité que vous exploitez déjà. Un seul artefact se comporte de façon identique, du portable au centre de données air-gapped.', + action: 'Guide de déploiement', + }, + security: { + title: 'Sécurité & conformité', + description: 'Les agents IA ne peuvent jamais voir ni faire plus que l’utilisateur derrière eux. SSO, RBAC, masquage au niveau des champs et audit complet sont appliqués dans le runtime — et s’appliquent à l’identique au trafic humain et au trafic IA.', + action: 'Modèle de permissions', + }, + business: { + title: 'Responsables métier', + description: 'Offrez à vos équipes une IA qui connaît vraiment votre CRM, vos contrats, vos tickets — parce qu’elle dialogue avec le système de référence, pas avec une copie. Les données clients ne quittent jamais votre entreprise.', + action: 'Vue d’ensemble de l’architecture', + }, + }, + deployModes: { + eyebrow: '01 — Déployer', + heading: 'S’exécute là où vivent déjà vos données', + subheading: 'Même artefact, même comportement. Choisissez la forme qui correspond à votre infrastructure — ObjectOS, et l’accès IA qui l’accompagne, reste à l’intérieur de votre réseau.', + docker: { + title: 'Docker', + tagline: 'Un hôte, une seule commande', + description: 'Un conteneur connecté à votre propre base de données. Idéal pour l’évaluation, les outils internes et les déploiements en petite équipe.', + href: '/docs/deploy/docker', + cta: 'Guide Docker', + }, + kubernetes: { + title: 'Kubernetes', + tagline: 'Haute disponibilité en production', + description: 'Deployment + Service + PersistentVolumeClaim, avec secrets et configuration issus des mécanismes que vous utilisez déjà. Chart Helm inclus.', + href: '/docs/deploy/kubernetes', + cta: 'Guide Kubernetes', + }, + airGapped: { + title: 'Air-gapped', + tagline: 'Aucune sortie internet', + description: 'Livrez un bundle de release dans un réseau sans connectivité publique. ObjectOS lit son artefact depuis le disque et ne contacte jamais l’extérieur — y compris la surface d’intégration IA.', + href: '/docs/deploy/air-gapped', + cta: 'Guide air-gapped', + }, + }, + bottomCta: { + heading: 'Donnez à l’IA un système métier qu’elle peut vraiment utiliser.', + subheading: 'Définissez vos objets une seule fois avec ObjectStack. Exécutez-les sur ObjectOS. Laissez l’IA travailler — sous vos règles, à l’intérieur de votre réseau.', + primary: 'Démarrage rapide', + primaryHref: '/docs/quickstart', + secondary: 'Architecture', + secondaryHref: '/docs/architecture', + }, + footer: { + copyright: '© 2026 ObjectStack AI LLC.', + privacy: 'Politique de confidentialité', + privacyHref: '/privacy', + terms: 'Conditions d’utilisation', + termsHref: '/terms', + }, + agentPreview: { + windowTitle: 'Console · Copilote de support', + userLabel: 'Vous', + agentLabel: 'Agent', + user1Line1: 'Quels tickets urgents sont ouverts depuis plus de 24 h,', + user1Line2: 'et qui en a la charge ?', + tool1Name: 'query_data', + tool1Target: ' · support_ticket', + matchSummary: ' 3 tickets correspondent — groupés par responsable :', + ticket1Meta: ' · ouvert 31h · Maya ', + ticket1Note: '(panne de facturation)', + ticket2Meta: ' · ouvert 28h · Maya ', + ticket2Note: '(échec SSO)', + ticket3Meta: ' · ouvert 26h · Jordan', + ticket3Note: ' (bug d’export)', + insight: ' Maya en porte 2/3 — probablement le goulot d’étranglement.', + user2: 'Escalade les trois et réassigne ceux de Maya à l’astreinte.', + tool2Name: 'action_escalate_ticket', + tool2Count: ' ×3 · ', + tool2Sep: '', + tool3Name: 'action_reassign', + tool3Count: ' ×2', + actionResult: ' ✓ priorité → urgent · astreinte notifiée · audité', + governance: ' S’exécute en votre nom. Permissions + règles de champs appliquées.', + }, +}; + +/** + * Korean (한국어) Translations + */ +export const ko: HomepageTranslations = { + badge: { + status: 'AI 네이티브 비즈니스 플랫폼', + version: SPEC_VERSION, + }, + hero: { + title: { + line1: 'AI가 실제로 쓸 수 있는', + line2: '비즈니스 플랫폼.', + }, + subtitle: { + line1: 'ObjectOS는 여러분의 비즈니스 시스템을 위한 AI 네이티브 런타임입니다 — CRM, 계약, 티켓, 승인 등 ObjectStack 메타데이터 프로토콜로 모델링된 모든 것을 담습니다.', + line2: '비즈니스 객체를 ObjectOS 위에 올리면, AI 에이전트가 그 데이터를 안전하게 조회하고 분석하고 실행할 수 있습니다 — 여러분의 권한 아래, 여러분의 서버에서, 모든 단계가 감사된 채로.', + }, + cta: { + primary: '작동 방식 보기', + primaryHref: '/docs/architecture', + secondary: '빠른 시작', + secondaryHref: '/docs/quickstart', + }, + quickStart: { + label: '터미널', + commands: [ + 'npm i -g @objectstack/cli', + 'os start', + ], + }, + trustStrip: { + license: 'MIT 라이선스', + selfHosted: '자체 호스팅', + version: OBJECTOS_VERSION, + runtime: 'Docker · Kubernetes · 에어갭', + }, + }, + features: { + connectSystems: { + title: '이미 운영 중인 시스템을 그대로 확장', + description: 'ObjectOS를 기존 비즈니스 데이터베이스에 연결하고, 테이블을 객체로 모델링한 뒤, 그 위에 AI 네이티브 조회·분석·자동화를 더하세요 — 기록 시스템을 옮길 필요가 없습니다. 코딩 에이전트가 스키마를 스캔해 객체를 대신 생성해 줄 수 있습니다.', + }, + selfHosted: { + title: '모든 객체가 곧 AI 도구', + description: 'ObjectStack 메타데이터에서 비즈니스 객체를 한 번 정의하면, ObjectOS가 자동으로 그것을 거버넌스된, 호출 가능한 도구로 AI 에이전트에 노출합니다 — 글루 코드도, 따로 유지보수할 통합 계층도 필요 없습니다.', + }, + deployAnywhere: { + title: 'AI는 로그인한 사용자로서 행동합니다', + description: '에이전트가 데이터를 조회하거나 업데이트할 때, 그것은 호출자의 신원으로 실행됩니다. 그 사람이 보거나 할 수 있는 것까지만 에이전트도 할 수 있고, 그 이상은 안 됩니다. 이 경계는 프롬프트가 아니라 런타임에서 강제됩니다.', + }, + airGapped: { + title: '모든 작업이 감사됩니다', + description: '모든 읽기, 모든 쓰기, 모든 에스컬레이션 — 사람이든 에이전트든 — 은 누가, 무엇을, 언제, 왜 했는지와 함께 기록됩니다. 컴플라이언스는 두 개가 아니라 하나의 로그만 보면 됩니다.', + }, + identity: { + title: '여러분의 신원 체계에 그대로 연결', + description: 'OAuth, OIDC, SAML, 기업 SSO 또는 로컬 계정. AI 세션은 동일한 신원, 동일한 MFA, 동일한 오프보딩을 그대로 따릅니다 — 따로 관리할 "AI 계정" 같은 건 없습니다.', + }, + permissions: { + title: '권한은 런타임에서 강제됩니다', + description: '역할 기반 접근, 레코드 수준 규칙, 필드 수준 마스킹이 ObjectOS 내부에서 실행됩니다 — 요청이 UI에서 오든, API 클라이언트에서 오든, AI 에이전트에서 오든 동일한 정책이 적용됩니다.', + }, + observability: { + title: '여러분의 데이터, 여러분의 네트워크', + description: 'ObjectOS는 여러분의 환경에서 실행됩니다 — 프라이빗 클라우드, 온프레미스, 또는 완전한 에어갭. 비즈니스 데이터와 AI 프롬프트는 여러분의 경계 안에 머뭅니다. 중간에 제3자는 없습니다.', + }, + }, + capabilities: { + eyebrow: '02 — 어떻게 안전한가', + heading: '통제를 포기하지 않는 AI 접근', + subheading: '플랫폼이 이미 갖춘 모든 보호 장치 — 신원, 권한, 감사, 네트워크 경계 — 가 에이전트가 데이터에 닿는 그 순간 적용됩니다. 새로 관리할 것은 없습니다.', + }, + personas: { + eyebrow: '03 — 누구를 위한 것인가', + heading: '결국 승인해야 하는 세 사람을 위해 설계', + itOps: { + title: 'IT 및 플랫폼 운영자', + description: 'Docker 이미지와 Helm 차트로 제공됩니다. 이미 운영 중인 데이터베이스, 신원, 관측성 스택에 그대로 연결됩니다. 하나의 산출물이 노트북에서든 에어갭 데이터 센터에서든 동일하게 실행됩니다.', + action: '배포 가이드', + }, + security: { + title: '보안 및 컴플라이언스', + description: 'AI 에이전트는 그 뒤의 사용자보다 더 보거나 더 할 수 없습니다. SSO, RBAC, 필드 수준 마스킹, 완전한 감사가 런타임에서 강제되며 — 사람과 AI 트래픽에 동일하게 적용됩니다.', + action: '권한 모델', + }, + business: { + title: '비즈니스 책임자', + description: '팀에게 여러분의 CRM, 계약, 티켓을 진짜로 아는 AI를 주세요 — 복사본이 아니라 기록 시스템과 직접 대화하기 때문입니다. 고객 데이터는 결코 회사 밖으로 나가지 않습니다.', + action: '아키텍처 개요', + }, + }, + deployModes: { + eyebrow: '01 — 배포', + heading: '데이터가 이미 있는 곳에서 실행', + subheading: '동일한 산출물, 동일한 동작. 여러분의 인프라에 맞는 형태를 고르세요 — ObjectOS와 그것이 가져오는 AI 접근은 여러분의 네트워크 안에 머뭅니다.', + docker: { + title: 'Docker', + tagline: '단일 호스트, 단일 명령', + description: '여러분의 데이터베이스에 연결되는 컨테이너 하나. 평가, 내부 도구, 소규모 팀 배포에 이상적입니다.', + href: '/docs/deploy/docker', + cta: 'Docker 가이드', + }, + kubernetes: { + title: 'Kubernetes', + tagline: '프로덕션 HA', + description: 'Deployment + Service + PersistentVolumeClaim, 시크릿과 설정은 이미 쓰고 있는 방식 그대로. Helm 차트 포함.', + href: '/docs/deploy/kubernetes', + cta: 'Kubernetes 가이드', + }, + airGapped: { + title: '에어갭', + tagline: '인터넷 송신 없음', + description: '공개 연결이 전혀 없는 네트워크로 릴리스 번들을 들여보내세요. ObjectOS는 디스크에서 산출물을 읽으며 절대 외부로 호출하지 않습니다 — AI 통합 영역도 마찬가지입니다.', + href: '/docs/deploy/air-gapped', + cta: '에어갭 가이드', + }, + }, + bottomCta: { + heading: 'AI에게 진짜로 쓸 수 있는 비즈니스 시스템을 주세요.', + subheading: 'ObjectStack으로 객체를 한 번 정의하고, ObjectOS에서 실행하세요. AI가 일하게 하세요 — 여러분의 규칙 아래, 여러분의 네트워크 안에서.', + primary: '빠른 시작', + primaryHref: '/docs/quickstart', + secondary: '아키텍처', + secondaryHref: '/docs/architecture', + }, + footer: { + copyright: '© 2026 ObjectStack AI LLC.', + privacy: '개인정보 처리방침', + privacyHref: '/privacy', + terms: '서비스 약관', + termsHref: '/terms', + }, + agentPreview: { + windowTitle: 'Console · 고객지원 코파일럿', + userLabel: '나', + agentLabel: 'Agent', + user1Line1: '24시간 넘게 열려 있는 긴급 티켓은 어떤 거고,', + user1Line2: '각각 누가 맡고 있어?', + tool1Name: 'query_data', + tool1Target: ' · support_ticket', + matchSummary: ' 3건 일치 — 담당자별 그룹화:', + ticket1Meta: ' · 31h 열림 · Maya ', + ticket1Note: '(결제 장애)', + ticket2Meta: ' · 28h 열림 · Maya ', + ticket2Note: '(SSO 실패)', + ticket3Meta: ' · 26h 열림 · Jordan', + ticket3Note: ' (내보내기 버그)', + insight: ' Maya가 3건 중 2건을 떠안고 있음 — 병목일 가능성이 큼.', + user2: '세 건 모두 에스컬레이션하고, Maya 건은 온콜에게 재배정해.', + tool2Name: 'action_escalate_ticket', + tool2Count: ' ×3 · ', + tool2Sep: '', + tool3Name: 'action_reassign', + tool3Count: ' ×2', + actionResult: ' ✓ 우선순위 → 긴급 · 온콜 통보 완료 · 감사됨', + governance: ' 나로서 실행됨. 권한 + 필드 규칙 그대로 적용.', + }, +}; + /** * Registry of available homepage translations. Locales absent here * (ja, de, es, fr until translated) resolve to English via the getter. @@ -494,6 +1301,11 @@ export const zhHans: HomepageTranslations = { const HOMEPAGE_TRANSLATIONS: Partial> = { en, 'zh-Hans': zhHans, + ja, + de, + es, + fr, + ko, }; /** diff --git a/apps/docs/lib/i18n.ts b/apps/docs/lib/i18n.ts index c7be422..312f716 100644 --- a/apps/docs/lib/i18n.ts +++ b/apps/docs/lib/i18n.ts @@ -10,10 +10,11 @@ import { defineI18n } from 'fumadocs-core/i18n'; * - de: German (Deutsch) * - es: Spanish (Español) * - fr: French (Français) + * - ko: Korean (한국어) */ export const i18n = defineI18n({ defaultLanguage: 'en', - languages: ['en', 'zh-Hans', 'ja', 'de', 'es', 'fr'], + languages: ['en', 'zh-Hans', 'ja', 'de', 'es', 'fr', 'ko'], // Hide locale prefix for default language (e.g., /docs instead of /en/docs) hideLocale: 'default-locale', }); diff --git a/apps/docs/lib/layout.shared.tsx b/apps/docs/lib/layout.shared.tsx index d808146..b1e6341 100644 --- a/apps/docs/lib/layout.shared.tsx +++ b/apps/docs/lib/layout.shared.tsx @@ -7,13 +7,14 @@ export const gitConfig = { branch: 'main', }; -const NAV_LABELS: Record = { - en: { docs: 'Docs', download: 'Download', changelog: 'Changelog' }, - 'zh-Hans': { docs: '文档', download: '下载', changelog: '更新日志' }, - ja: { docs: 'ドキュメント', download: 'ダウンロード', changelog: '変更履歴' }, - de: { docs: 'Dokumentation', download: 'Download', changelog: 'Änderungen' }, - es: { docs: 'Documentación', download: 'Descargar', changelog: 'Cambios' }, - fr: { docs: 'Documentation', download: 'Télécharger', changelog: 'Journal' }, +const NAV_LABELS: Record = { + en: { docs: 'Docs', blog: 'Blog', download: 'Download', changelog: 'Changelog' }, + 'zh-Hans': { docs: '文档', blog: '博客', download: '下载', changelog: '更新日志' }, + ja: { docs: 'ドキュメント', blog: 'ブログ', download: 'ダウンロード', changelog: '変更履歴' }, + de: { docs: 'Dokumentation', blog: 'Blog', download: 'Download', changelog: 'Änderungen' }, + es: { docs: 'Documentación', blog: 'Blog', download: 'Descargar', changelog: 'Cambios' }, + fr: { docs: 'Documentation', blog: 'Blog', download: 'Télécharger', changelog: 'Journal' }, + ko: { docs: '문서', blog: '블로그', download: '다운로드', changelog: '변경 내역' }, }; const RELEASES_URL = 'https://github.com/objectstack-ai/objectos/releases'; @@ -48,6 +49,11 @@ export function baseOptions(lang: string = 'en'): BaseLayoutProps { url: `${prefix}/docs`, active: 'nested-url', }, + { + text: labels.blog, + url: `${prefix}/blog`, + active: 'nested-url', + }, { text: labels.download, url: `${prefix}/download`, diff --git a/apps/docs/lib/source.ts b/apps/docs/lib/source.ts index ef28c0f..d3c502f 100644 --- a/apps/docs/lib/source.ts +++ b/apps/docs/lib/source.ts @@ -12,6 +12,7 @@ export const source = loader({ export const blog = loader({ baseUrl: '/blog', + i18n, source: blogCollection.toFumadocsSource(), }); diff --git a/apps/docs/middleware.ts b/apps/docs/middleware.ts index bd162dc..3d15e7d 100644 --- a/apps/docs/middleware.ts +++ b/apps/docs/middleware.ts @@ -31,6 +31,8 @@ const LANGUAGE_MAPPING: Record = { // Traditional variants fall back to Simplified until zh-Hant ships 'zh-TW': 'zh-Hans', 'zh-HK': 'zh-Hans', + 'ko': 'ko', // Korean + 'ko-KR': 'ko', // Korean (Korea) -> Korean }; /** diff --git a/content/blog/extend-existing-systems-with-ai.de.mdx b/content/blog/extend-existing-systems-with-ai.de.mdx new file mode 100644 index 0000000..a2e5c10 --- /dev/null +++ b/content/blog/extend-existing-systems-with-ai.de.mdx @@ -0,0 +1,124 @@ +--- +title: "Mach dein bestehendes Geschäftssystem KI-nativ — ohne Migration" +description: "Verbinde ObjectOS mit der Datenbank, die du bereits betreibst, modelliere die Tabellen als Objekte und lass KI echte Daten abfragen und bearbeiten — unter deinen Berechtigungen, auf deinen Servern." +author: ObjectStack Team +date: 2026-05-30 +tags: + - Data Sources + - AI-Native + - Architecture +--- + +Die meisten „Bring KI in dein Geschäft"-Versprechen setzen stillschweigend +einen Neuaufbau voraus: die Daten in eine neue Plattform heben, die +Workflows neu implementieren, das Team neu schulen. Genau das will niemand. +Das System of Record — das CRM, das ERP, das Ticketing-Tool, das +selbstgebaute Back Office — funktioniert bereits, und genau dort liegen die +Daten schon. + +ObjectOS vertritt die gegenteilige Haltung. **Du migrierst nicht. Du verbindest.** + +## Die Idee in einem Satz + +Richte ObjectOS auf deine bestehende Datenbank, beschreibe die Tabellen, die +dich interessieren, als Objekte, und jeder KI-Agent, jeder Flow und jedes +Dashboard arbeitet sofort mit diesen Daten — automatisch an das richtige +System geroutet, geregelt durch dieselben Berechtigungen, die deine Nutzer +bereits haben. + +Die ursprüngliche Anwendung ändert sich nicht. Die Zeilen wandern nicht. +ObjectOS wird zur KI-nativen, berechtigungsbewussten Oberfläche über dem, +was du bereits betreibst. + +## Wie es heute funktioniert + +Es gibt vier Schritte, und keiner davon lautet „bau deine App neu": + +1. **Verbinde die Datenbank als Datasource.** Eine Datasource ist eine + benannte Verbindung — Postgres, MySQL, MongoDB, SQLite. Die + Zugangsdaten stammen aus der Umgebung, und die Verbindung kann + schreibgeschützt sein, wenn du zunächst nur analysieren möchtest. + + ```ts + import type { Datasource } from '@objectstack/spec'; + + export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + active: true, + }; + ``` + +2. **Modelliere die Tabellen als Objekte** — und du musst sie nicht von + Hand eintippen. Richte einen Coding-Agenten wie Claude Code auf das + verbundene Schema und bitte ihn, pro Tabelle eine Objektdatei auf + Quellebene zu generieren. Unsere + [`hotcrm`](https://github.com/objectstack-ai/hotcrm)-Referenz-App + zeigt die genaue Form dieser Ausgabe: ein `ObjectSchema.create({ … })` + mit typisierten `Field.*`-Definitionen und `Field.lookup(...)` für + Fremdschlüssel. Du prüfst und verfeinerst; das Ergebnis gehört dir. + +3. **Binde Objekte an die Datasource** — pro Objekt oder mit einer + Routing-Regel für einen ganzen Namespace: + + ```ts + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ] + ``` + +4. **Lass die KI arbeiten.** Sobald eine Tabelle ein Objekt ist, arbeitet + die KI-Schicht kostenlos damit. Agenten fragen über ObjectQL ab, das + jedes Objekt zu seiner Datenbank routet — und jede Abfrage läuft als der + **angemeldete Nutzer** und befolgt Berechtigungen auf Objekt-, Datensatz- + und Feldebene. + +## Warum das sicher ist + +Der Einwand gegen „KI auf unseren Geschäftsdaten" lautet immer Governance, +und genau dort leistet die Runtime die Arbeit: + +- **KI handelt als der Nutzer, nie über ihm.** Ein Agent sieht genau das, + was die Person dahinter sehen darf — durchgesetzt in der Runtime, nicht + im Prompt. +- **Standardmäßig schreibgeschützt, wenn du es möchtest.** Binde Objekte an + eine schreibgeschützte Verbindung oder einen schreibgeschützten DB-Nutzer. + Analysiere die Produktion sicher; aktiviere Schreibzugriffe bewusst. +- **Alles auditiert.** Jeder Lese-, Schreib- und Eskalationsvorgang — ob + Mensch oder Agent — wird mit Wer, Was, Wann und Warum aufgezeichnet. +- **Deine Daten bleiben in deinem Netzwerk.** ObjectOS läuft in deiner + Umgebung. Geschäftsdaten und Prompts verlassen deinen Perimeter nie. + +## Was ausgeliefert ist vs. was kommt + +Der oben beschriebene Ablauf funktioniert **heute** mit ausgelieferten +Bausteinen: Datasources, Routing pro Objekt, capability-bewusster +ObjectQL-Pushdown und die KI-Agenten-Schicht. Die Objektgenerierung erfolgt +mit einem Coding-Agenten gegen dein verbundenes Schema — auf dieselbe Weise, +wie die `hotcrm`-Objekte erstellt wurden. + +Eine reichhaltigere, **schlüsselfertige Föderation** — einstufiger +Schema-Import, Bindung an extern verwaltete Schemata und eingebaute +Schreibsicherheits-Gates — befindet sich in aktiver Konzeption unter +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(Status: *Proposed*). Wir schreiben mehr dazu, sobald es verfügbar ist. Bis +dahin ist der dokumentierte Weg der unterstützte. + +## Probier es aus + +- [Data Sources](/docs/configure/data-sources) — der vollständige Authoring-Leitfaden +- [Bestehende Systeme erweitern](/docs/extend-existing-systems) — das Szenario, von Anfang bis Ende +- [KI-Agenten](/docs/build/agents) — deklarative Agenten über deinen Objekten + +Wenn du bereits eine Geschäftsdatenbank hast, bist du fast am Ziel. Verbinde +sie, modelliere ein paar Tabellen und stell deinen Daten eine Frage. diff --git a/content/blog/extend-existing-systems-with-ai.es.mdx b/content/blog/extend-existing-systems-with-ai.es.mdx new file mode 100644 index 0000000..a8351dc --- /dev/null +++ b/content/blog/extend-existing-systems-with-ai.es.mdx @@ -0,0 +1,121 @@ +--- +title: "Haz que tu sistema de negocio existente sea AI-Native — sin una migración" +description: "Conecta ObjectOS a la base de datos que ya ejecutas, modela las tablas como objetos, y deja que la IA consulte y actúe sobre datos reales — bajo tus permisos, en tus servidores." +author: ObjectStack Team +date: 2026-05-30 +tags: + - Data Sources + - AI-Native + - Architecture +--- + +La mayoría de las propuestas de "añade IA a tu negocio" asumen en silencio una +reconstrucción: levantar los datos a una nueva plataforma, reimplementar los +flujos de trabajo, reentrenar al equipo. Esa es la parte que nadie quiere. El +sistema de registro — el CRM, el ERP, la herramienta de tickets, el back office +hecho en casa — ya funciona, y ya es donde viven los datos. + +ObjectOS adopta la postura opuesta. **No migras. Conectas.** + +## La idea en una sola frase + +Apunta ObjectOS a tu base de datos existente, describe las tablas que te +importan como objetos, y cada agente de IA, flujo y dashboard funciona de +inmediato sobre esos datos — enrutados automáticamente al sistema correcto, +gobernados por los mismos permisos que tus usuarios ya tienen. + +La aplicación original no cambia. Las filas no se mueven. ObjectOS se convierte +en la superficie AI-native y consciente de los permisos sobre lo que ya +ejecutas. + +## Cómo funciona hoy + +Hay cuatro pasos, y ninguno de ellos es "reconstruye tu app": + +1. **Conecta la base de datos como un datasource.** Un datasource es una + conexión con nombre — Postgres, MySQL, MongoDB, SQLite. Las credenciales + provienen del entorno, y la conexión puede ser de solo lectura si solo + quieres analizar primero. + + ```ts + import type { Datasource } from '@objectstack/spec'; + + export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + active: true, + }; + ``` + +2. **Modela las tablas como objetos** — y no tienes que escribirlas a mano. + Apunta un agente de programación como Claude Code al esquema conectado y + pídele que genere un archivo de objeto a nivel de fuente por tabla. Nuestra + app de referencia [`hotcrm`](https://github.com/objectstack-ai/hotcrm) + muestra la forma exacta que adopta esa salida: un `ObjectSchema.create({ … })` + con definiciones tipadas de `Field.*` y `Field.lookup(...)` para las claves + foráneas. Tú revisas y refinas; tú eres dueño del resultado. + +3. **Vincula los objetos al datasource** — por objeto, o con una regla de + enrutamiento para todo un namespace: + + ```ts + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ] + ``` + +4. **Deja que la IA trabaje.** En el momento en que una tabla es un objeto, la + capa de IA trabaja sobre ella de forma gratuita. Los agentes consultan a + través de ObjectQL, que enruta cada objeto a su base de datos — y cada + consulta se ejecuta como el **usuario autenticado**, obedeciendo los permisos + a nivel de objeto, de registro y de campo. + +## Por qué esto es seguro + +La objeción a "IA sobre los datos de nuestro negocio" siempre es la gobernanza, +y ahí es exactamente donde el runtime hace el trabajo: + +- **La IA actúa como el usuario, nunca por encima de él.** Un agente ve + exactamente lo que la persona detrás de él tiene permitido ver — aplicado en + el runtime, no en el prompt. +- **Solo lectura por defecto si así lo quieres.** Vincula los objetos a una + conexión o usuario de BD de solo lectura. Analiza producción de forma segura; + habilita las escrituras deliberadamente. +- **Todo auditado.** Cada lectura, escritura y escalación — humana o de agente — + se registra con quién, qué, cuándo y por qué. +- **Tus datos permanecen en tu red.** ObjectOS se ejecuta en tu entorno. Los + datos de negocio y los prompts nunca salen de tu perímetro. + +## Lo que ya está disponible vs. lo que viene + +El flujo anterior funciona **hoy** con bloques de construcción ya disponibles: +datasources, enrutamiento por objeto, pushdown de ObjectQL consciente de +capacidades y la capa de agentes de IA. La generación de objetos se hace con un +agente de programación contra tu esquema conectado — de la misma forma en que se +crearon los objetos de `hotcrm`. + +Una experiencia de **federación llave en mano** más completa — importación de +esquema en un solo paso, vinculación a esquemas de propiedad externa y +compuertas de seguridad de escritura integradas — está en diseño activo bajo +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(estado: *Propuesto*). Escribiremos más a medida que se materialice. Hasta +entonces, el camino documentado es el camino soportado. + +## Pruébalo + +- [Data Sources](/docs/configure/data-sources) — la guía completa de creación +- [Extend Existing Systems](/docs/extend-existing-systems) — el escenario, de principio a fin +- [AI Agents](/docs/build/agents) — agentes declarativos sobre tus objetos + +Si ya tienes una base de datos de negocio, estás a buena parte del camino. +Conéctala, modela unas cuantas tablas y hazle una pregunta a tus datos. diff --git a/content/blog/extend-existing-systems-with-ai.fr.mdx b/content/blog/extend-existing-systems-with-ai.fr.mdx new file mode 100644 index 0000000..8f894d0 --- /dev/null +++ b/content/blog/extend-existing-systems-with-ai.fr.mdx @@ -0,0 +1,130 @@ +--- +title: "Rendez votre système métier existant AI-native — sans migration" +description: "Connectez ObjectOS à la base de données que vous exploitez déjà, modélisez les tables comme des objets, et laissez l'IA interroger et agir sur des données réelles — sous vos permissions, sur vos serveurs." +author: ObjectStack Team +date: 2026-05-30 +tags: + - Data Sources + - AI-Native + - Architecture +--- + +La plupart des argumentaires « ajoutez de l'IA à votre activité » +présupposent discrètement une reconstruction : transférer les données +vers une nouvelle plateforme, réimplémenter les workflows, reformer +l'équipe. C'est précisément la partie dont personne ne veut. Le système +de référence — le CRM, l'ERP, l'outil de ticketing, le back-office maison +— fonctionne déjà, et c'est déjà là que vivent les données. + +ObjectOS adopte la position inverse. **Vous ne migrez pas. Vous +connectez.** + +## L'idée en une phrase + +Pointez ObjectOS vers votre base de données existante, décrivez comme des +objets les tables qui vous intéressent, et chaque agent IA, flux et +tableau de bord fonctionne immédiatement sur ces données — routé +automatiquement vers le bon système, gouverné par les mêmes permissions +que vos utilisateurs possèdent déjà. + +L'application d'origine ne change pas. Les lignes ne bougent pas. +ObjectOS devient la surface AI-native et sensible aux permissions +au-dessus de ce que vous exploitez déjà. + +## Comment cela fonctionne aujourd'hui + +Il y a quatre étapes, et aucune d'elles n'est « reconstruire votre +application » : + +1. **Connectez la base de données comme datasource.** Une datasource est + une connexion nommée — Postgres, MySQL, MongoDB, SQLite. Les + identifiants proviennent de l'environnement, et la connexion peut être + en lecture seule si vous voulez d'abord vous contenter d'analyser. + + ```ts + import type { Datasource } from '@objectstack/spec'; + + export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + active: true, + }; + ``` + +2. **Modélisez les tables comme des objets** — et vous n'avez pas à les + saisir à la main. Pointez un agent de codage comme Claude Code vers le + schéma connecté et demandez-lui de générer un fichier objet au niveau + source par table. Notre application de référence + [`hotcrm`](https://github.com/objectstack-ai/hotcrm) montre la forme + exacte que prend cette sortie : un `ObjectSchema.create({ … })` avec + des définitions `Field.*` typées et `Field.lookup(...)` pour les clés + étrangères. Vous révisez et affinez ; le résultat vous appartient. + +3. **Liez les objets à la datasource** — par objet, ou avec une règle de + routage pour tout un namespace : + + ```ts + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ] + ``` + +4. **Laissez l'IA travailler.** Dès qu'une table est un objet, la couche + IA travaille dessus gratuitement. Les agents interrogent via ObjectQL, + qui route chaque objet vers sa base de données — et chaque requête + s'exécute en tant qu'**utilisateur connecté**, en respectant les + permissions au niveau objet, enregistrement et champ. + +## Pourquoi c'est sûr + +L'objection à « de l'IA sur nos données métier » est toujours la +gouvernance, et c'est exactement là que le runtime fait le travail : + +- **L'IA agit en tant qu'utilisateur, jamais au-dessus de lui.** Un agent + voit précisément ce que la personne derrière lui est autorisée à voir — + appliqué dans le runtime, pas dans le prompt. +- **Lecture seule par défaut si vous le souhaitez.** Liez les objets à + une connexion ou un utilisateur de base de données en lecture seule. + Analysez la production en toute sécurité ; activez les écritures + délibérément. +- **Tout est audité.** Chaque lecture, écriture et escalade — humaine ou + agent — est enregistrée avec qui, quoi, quand et pourquoi. +- **Vos données restent dans votre réseau.** ObjectOS s'exécute dans + votre environnement. Les données métier et les prompts ne quittent + jamais votre périmètre. + +## Ce qui est livré vs. ce qui arrive + +Le flux ci-dessus fonctionne **aujourd'hui** avec des briques déjà +livrées : datasources, routage par objet, pushdown ObjectQL sensible aux +capacités, et la couche d'agents IA. La génération des objets se fait avec +un agent de codage sur votre schéma connecté — de la même manière que les +objets `hotcrm` ont été rédigés. + +Une expérience de **fédération clé en main** plus riche — import de schéma +en une étape, liaison à des schémas détenus en externe et garde-fous +d'écriture intégrés — est en cours de conception sous +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(statut : *Proposé*). Nous en écrirons davantage à mesure que cela se +concrétisera. D'ici là, le chemin documenté est celui qui est pris en +charge. + +## Essayez-le + +- [Data Sources](/docs/configure/data-sources) — le guide de rédaction complet +- [Étendre les systèmes existants](/docs/extend-existing-systems) — le scénario, de bout en bout +- [Agents IA](/docs/build/agents) — des agents déclaratifs sur vos objets + +Si vous disposez déjà d'une base de données métier, vous êtes presque +arrivé. Connectez-la, modélisez quelques tables, et posez une question à +vos données. diff --git a/content/blog/extend-existing-systems-with-ai.ja.mdx b/content/blog/extend-existing-systems-with-ai.ja.mdx new file mode 100644 index 0000000..4ccbf0c --- /dev/null +++ b/content/blog/extend-existing-systems-with-ai.ja.mdx @@ -0,0 +1,117 @@ +--- +title: "既存の業務システムを移行なしで AI ネイティブにする" +description: "ObjectOS をすでに稼働しているデータベースに接続し、テーブルをオブジェクトとしてモデル化すれば、AI が実データをクエリし操作できます — あなたの権限のもとで、あなたのサーバー上で。" +author: ObjectStack Team +date: 2026-05-30 +tags: + - Data Sources + - AI-Native + - Architecture +--- + +「業務に AI を追加しましょう」という売り込みのほとんどは、暗黙のうちに作り直しを +前提としています。データを新しいプラットフォームに移し、ワークフローを再実装し、 +チームを再教育する。それこそ誰もやりたくない部分です。システムオブレコード — CRM、 +ERP、チケット管理ツール、内製のバックオフィス — はすでに動いており、データもすでに +そこにあります。 + +ObjectOS は正反対の立場を取ります。**移行はしません。接続するのです。** + +## 一文で言うと + +ObjectOS を既存のデータベースに向け、関心のあるテーブルをオブジェクトとして +記述すれば、あらゆる AI エージェント、フロー、ダッシュボードが即座にそのデータ上で +動作します — 適切なシステムへ自動的にルーティングされ、ユーザーがすでに持っている +権限と同じものによって統制されます。 + +元のアプリケーションは変わりません。レコードは動きません。ObjectOS は、あなたが +すでに稼働しているものの上に乗る、AI ネイティブで権限を認識するサーフェスになります。 + +## 現状の仕組み + +手順は 4 つあり、そのどれもが「アプリを作り直す」ものではありません。 + +1. **データベースをデータソースとして接続する。** データソースとは名前付きの + 接続です — Postgres、MySQL、MongoDB、SQLite。認証情報は環境から取得され、 + まず分析だけしたい場合は接続を読み取り専用にできます。 + + ```ts + import type { Datasource } from '@objectstack/spec'; + + export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + active: true, + }; + ``` + +2. **テーブルをオブジェクトとしてモデル化する** — しかも手で入力する必要は + ありません。Claude Code のようなコーディングエージェントを接続済みのスキーマに + 向け、テーブルごとに 1 つのソースレベルのオブジェクトファイルを生成するよう + 依頼してください。私たちの + [`hotcrm`](https://github.com/objectstack-ai/hotcrm) リファレンスアプリは、 + その出力が取る正確なかたちを示しています。型付けされた `Field.*` 定義と外部キー用の + `Field.lookup(...)` を備えた `ObjectSchema.create({ … })` です。あなたはレビューして + 洗練させ、その結果を所有します。 + +3. **オブジェクトをデータソースに紐付ける** — オブジェクトごとに、あるいは + 名前空間全体に対するルーティングルールで: + + ```ts + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ] + ``` + +4. **AI に働かせる。** テーブルがオブジェクトになった瞬間、AI レイヤーは追加コスト + なしでその上で動作します。エージェントは ObjectQL を介してクエリし、ObjectQL は各 + オブジェクトをそのデータベースへルーティングします — そしてすべてのクエリは + **サインイン済みユーザー** として実行され、オブジェクトレベル、レコードレベル、 + フィールドレベルの権限に従います。 + +## なぜ安全なのか + +「私たちの業務データに AI を」という反論は常にガバナンスに行き着きますが、まさに +そこをランタイムが担います。 + +- **AI はユーザーとして振る舞い、決してその上には立たない。** エージェントは、その + 背後にいる人が見ることを許されているものだけを正確に見ます — プロンプトではなく + ランタイムで強制されます。 +- **必要なら既定で読み取り専用。** オブジェクトを読み取り専用の接続または DB ユーザーに + 紐付けてください。本番を安全に分析し、書き込みは意図的に有効化します。 +- **すべてが監査される。** すべての読み取り、書き込み、権限昇格は — 人間でも + エージェントでも — 誰が、何を、いつ、なぜ行ったかとともに記録されます。 +- **データはあなたのネットワーク内にとどまる。** ObjectOS はあなたの環境で動作します。 + 業務データとプロンプトがあなたの境界の外に出ることはありません。 + +## 出荷済みのもの vs. これから来るもの + +上記のフローは、出荷済みのビルディングブロックで **今日** 動作します。データソース、 +オブジェクトごとのルーティング、機能を認識する ObjectQL のプッシュダウン、そして AI +エージェントレイヤーです。オブジェクトの生成は、接続済みのスキーマに対してコーディング +エージェントで行われます — `hotcrm` のオブジェクトが書かれたのと同じ方法です。 + +よりリッチな **ターンキーのフェデレーション** 体験 — ワンステップのスキーマ +インポート、外部所有スキーマへの紐付け、組み込みの書き込み安全ゲート — は +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +のもとで活発に設計中です(ステータス: *Proposed*)。実装が進み次第、より詳しく +書きます。それまでは、ドキュメント化された経路がサポート対象です。 + +## 試してみる + +- [Data Sources](/docs/configure/data-sources) — 完全な作成ガイド +- [Extend Existing Systems](/docs/extend-existing-systems) — シナリオ全体を端から端まで +- [AI Agents](/docs/build/agents) — あなたのオブジェクト上の宣言的エージェント + +すでに業務データベースをお持ちなら、もうほとんど準備は整っています。接続し、 +いくつかのテーブルをモデル化し、あなたのデータに問いかけてみてください。 diff --git a/content/blog/extend-existing-systems-with-ai.ko.mdx b/content/blog/extend-existing-systems-with-ai.ko.mdx new file mode 100644 index 0000000..e7d0153 --- /dev/null +++ b/content/blog/extend-existing-systems-with-ai.ko.mdx @@ -0,0 +1,121 @@ +--- +title: "마이그레이션 없이 기존 비즈니스 시스템을 AI 네이티브로 만들기" +description: "이미 운영 중인 데이터베이스에 ObjectOS를 연결하고, 테이블을 객체로 모델링하면, AI가 실제 데이터를 조회하고 작동하게 됩니다 — 여러분의 권한 아래, 여러분의 서버에서." +author: ObjectStack Team +date: 2026-05-30 +tags: + - Data Sources + - AI-Native + - Architecture +--- + +대부분의 "비즈니스에 AI를 더하세요"라는 제안은 은근히 재구축을 전제로 +합니다. 데이터를 새 플랫폼으로 옮기고, 워크플로우를 다시 구현하고, 팀을 +재교육합니다. 바로 그 부분을 아무도 원하지 않습니다. 시스템 오브 레코드 — +CRM, ERP, 티켓팅 도구, 자체 제작 백오피스 — 는 이미 작동하고 있으며, +데이터가 이미 그곳에 있습니다. + +ObjectOS는 정반대의 입장을 취합니다. **마이그레이션하지 않습니다. +연결합니다.** + +## 한 문장으로 요약한 아이디어 + +ObjectOS를 기존 데이터베이스에 연결하고, 관심 있는 테이블을 객체로 +기술하면, 모든 AI 에이전트, 플로우, 대시보드가 즉시 그 데이터 위에서 +작동합니다 — 알맞은 시스템으로 자동 라우팅되고, 사용자가 이미 가진 동일한 +권한에 의해 통제됩니다. + +원본 애플리케이션은 변하지 않습니다. 행은 이동하지 않습니다. ObjectOS는 +여러분이 이미 운영 중인 시스템 위에 자리하는 AI 네이티브, 권한 인식 표면이 +됩니다. + +## 오늘날 작동하는 방식 + +네 단계가 있으며, 그 어느 것도 "앱을 재구축하라"가 아닙니다. + +1. **데이터베이스를 데이터소스로 연결합니다.** 데이터소스는 이름이 부여된 + 연결입니다 — Postgres, MySQL, MongoDB, SQLite. 자격 증명은 환경에서 + 가져오며, 먼저 분석만 하고 싶다면 연결을 읽기 전용으로 설정할 수 + 있습니다. + + ```ts + import type { Datasource } from '@objectstack/spec'; + + export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + active: true, + }; + ``` + +2. **테이블을 객체로 모델링합니다** — 그리고 손으로 일일이 타이핑할 필요가 + 없습니다. Claude Code 같은 코딩 에이전트를 연결된 스키마에 연결하고 + 테이블당 소스 수준 객체 파일 하나를 생성하도록 요청하세요. 우리의 + [`hotcrm`](https://github.com/objectstack-ai/hotcrm) 레퍼런스 앱은 그 + 출력이 취하는 정확한 형태를 보여줍니다. 타입이 지정된 `Field.*` 정의와 + 외래 키를 위한 `Field.lookup(...)`을 갖춘 `ObjectSchema.create({ … })` + 입니다. 여러분은 검토하고 다듬습니다. 결과는 여러분의 것입니다. + +3. **객체를 데이터소스에 바인딩합니다** — 객체별로, 또는 네임스페이스 + 전체에 대한 라우팅 규칙으로: + + ```ts + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ] + ``` + +4. **AI가 작동하게 합니다.** 테이블이 객체가 되는 순간, AI 계층이 추가 + 작업 없이 그 위에서 작동합니다. 에이전트는 ObjectQL을 통해 조회하며, + ObjectQL은 각 객체를 해당 데이터베이스로 라우팅합니다 — 그리고 모든 + 쿼리는 **로그인한 사용자**로 실행되어, 객체 수준, 레코드 수준, 필드 수준 + 권한을 따릅니다. + +## 왜 이것이 안전한가 + +"우리 비즈니스 데이터 위의 AI"에 대한 반론은 언제나 거버넌스이며, 바로 +그곳에서 런타임이 일을 합니다. + +- **AI는 사용자로서 작동하며, 결코 그들 위에 있지 않습니다.** 에이전트는 + 그 뒤의 사람이 볼 수 있도록 허용된 것만 정확히 봅니다 — 프롬프트가 아니라 + 런타임에서 강제됩니다. +- **원한다면 기본적으로 읽기 전용.** 객체를 읽기 전용 연결 또는 DB + 사용자에 바인딩하세요. 프로덕션을 안전하게 분석하고, 쓰기는 의도적으로 + 활성화하세요. +- **모든 것이 감사됩니다.** 모든 읽기, 쓰기, 권한 상승 — 사람이든 + 에이전트든 — 은 누가, 무엇을, 언제, 왜 했는지와 함께 기록됩니다. +- **데이터는 여러분의 네트워크에 머뭅니다.** ObjectOS는 여러분의 환경에서 + 실행됩니다. 비즈니스 데이터와 프롬프트는 결코 여러분의 경계를 벗어나지 + 않습니다. + +## 출시된 것 vs. 다가오는 것 + +위의 흐름은 출시된 구성 요소들로 **오늘날** 작동합니다. 데이터소스, 객체별 +라우팅, 기능 인식 ObjectQL 푸시다운, 그리고 AI 에이전트 계층입니다. 객체 +생성은 연결된 스키마에 대해 코딩 에이전트로 수행됩니다 — `hotcrm` 객체가 +작성된 것과 동일한 방식입니다. + +더 풍부한 **턴키 페더레이션** 경험 — 원스텝 스키마 가져오기, 외부 소유 +스키마에 대한 바인딩, 내장 쓰기 안전 게이트 — 는 +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +아래에서 활발히 설계 중입니다(상태: *Proposed*). 그것이 도착하면 더 많이 +쓰겠습니다. 그때까지는, 문서화된 경로가 지원되는 경로입니다. + +## 사용해보기 + +- [데이터 소스](/docs/configure/data-sources) — 전체 작성 가이드 +- [기존 시스템 확장](/docs/extend-existing-systems) — 시나리오, 처음부터 끝까지 +- [AI 에이전트](/docs/build/agents) — 여러분의 객체 위의 선언적 에이전트 + +이미 비즈니스 데이터베이스가 있다면, 여러분은 이미 거의 다 온 것입니다. +그것을 연결하고, 몇 개의 테이블을 모델링한 다음, 데이터에게 질문을 던지세요. diff --git a/content/blog/extend-existing-systems-with-ai.mdx b/content/blog/extend-existing-systems-with-ai.mdx new file mode 100644 index 0000000..315590e --- /dev/null +++ b/content/blog/extend-existing-systems-with-ai.mdx @@ -0,0 +1,119 @@ +--- +title: Make Your Existing Business System AI-Native — Without a Migration +description: Connect ObjectOS to the database you already run, model the tables as objects, and let AI query and act on real data — under your permissions, on your servers. +author: ObjectStack Team +date: 2026-05-30 +tags: + - Data Sources + - AI-Native + - Architecture +--- + +Most "add AI to your business" pitches quietly assume a rebuild: lift the +data into a new platform, re-implement the workflows, re-train the team. +That's the part nobody wants. The system of record — the CRM, the ERP, +the ticketing tool, the homegrown back office — already works, and it's +already where the data lives. + +ObjectOS takes the opposite stance. **You don't migrate. You connect.** + +## The idea in one sentence + +Point ObjectOS at your existing database, describe the tables you care +about as objects, and every AI agent, flow, and dashboard immediately +works on that data — routed automatically to the right system, governed +by the same permissions your users already have. + +The original application doesn't change. The rows don't move. ObjectOS +becomes the AI-native, permission-aware surface on top of what you +already run. + +## How it works today + +There are four steps, and none of them is "rebuild your app": + +1. **Connect the database as a datasource.** A datasource is a named + connection — Postgres, MySQL, MongoDB, SQLite. Credentials come from + the environment, and the connection can be read-only if you only want + to analyze first. + + ```ts + import type { Datasource } from '@objectstack/spec'; + + export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + active: true, + }; + ``` + +2. **Model the tables as objects** — and you don't have to type them by + hand. Point a coding agent like Claude Code at the connected schema and + ask it to generate one source-level object file per table. Our + [`hotcrm`](https://github.com/objectstack-ai/hotcrm) reference app + shows the exact shape that output takes: an `ObjectSchema.create({ … })` + with typed `Field.*` definitions and `Field.lookup(...)` for foreign + keys. You review and refine; you own the result. + +3. **Bind objects to the datasource** — per object, or with a routing + rule for a whole namespace: + + ```ts + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ] + ``` + +4. **Let AI work.** The moment a table is an object, the AI layer works + on it for free. Agents query through ObjectQL, which routes each object + to its database — and every query runs as the **signed-in user**, + obeying object-, record-, and field-level permissions. + +## Why this is safe + +The objection to "AI on our business data" is always governance, and +that's exactly where the runtime does the work: + +- **AI acts as the user, never above them.** An agent sees precisely what + the person behind it is allowed to see — enforced in the runtime, not + the prompt. +- **Read-only by default if you want it.** Bind objects to a read-only + connection or DB user. Analyze production safely; enable writes + deliberately. +- **Everything audited.** Every read, write, and escalation — human or + agent — is recorded with who, what, when, and why. +- **Your data stays in your network.** ObjectOS runs in your environment. + Business data and prompts never leave your perimeter. + +## What's shipped vs. what's coming + +The flow above works **today** with shipped building blocks: datasources, +per-object routing, capability-aware ObjectQL pushdown, and the AI agent +layer. The object generation is done with a coding agent against your +connected schema — the same way the `hotcrm` objects were authored. + +A richer, **turn-key federation** experience — one-step schema import, +binding to externally owned schemas, and built-in write-safety gates — is +in active design under +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(status: *Proposed*). We'll write more as it lands. Until then, the +documented path is the supported one. + +## Try it + +- [Data Sources](/docs/configure/data-sources) — the full authoring guide +- [Extend Existing Systems](/docs/extend-existing-systems) — the scenario, end to end +- [AI Agents](/docs/build/agents) — declarative agents over your objects + +If you already have a business database, you're most of the way there. +Connect it, model a few tables, and ask your data a question. diff --git a/content/blog/extend-existing-systems-with-ai.zh-Hans.mdx b/content/blog/extend-existing-systems-with-ai.zh-Hans.mdx new file mode 100644 index 0000000..396d67c --- /dev/null +++ b/content/blog/extend-existing-systems-with-ai.zh-Hans.mdx @@ -0,0 +1,106 @@ +--- +title: 让你现有的业务系统原生支持 AI —— 无需迁移 +description: 把 ObjectOS 连到你已经在运行的数据库,将数据表建模为对象,让 AI 查询并操作真实数据 —— 在你的权限之下,在你的服务器之上。 +author: ObjectStack Team +date: 2026-05-30 +tags: + - Data Sources + - AI-Native + - Architecture +--- + +大多数"给你的业务加上 AI"的说辞,都悄悄假设了一次重建:把数据搬到一个新平台, +重新实现工作流,重新培训团队。而这正是没人想做的部分。那套记录系统 —— +CRM、ERP、工单工具、自研的后办公系统 —— 已经在运转,数据也已经在那里。 + +ObjectOS 采取相反的立场。**你不迁移。你连接。** + +## 一句话讲清这个想法 + +把 ObjectOS 指向你现有的数据库,将你关心的数据表描述为对象,于是每一个 AI +Agent、流程和仪表盘都立刻在这些数据上工作 —— 自动路由到正确的系统,并受你的 +用户已经拥有的同一套权限治理。 + +原有的应用不会改变。数据行不会移动。ObjectOS 成为你已运行系统之上那层原生支持 +AI、感知权限的界面。 + +## 它今天如何运作 + +一共四步,而且没有一步是"重建你的应用": + +1. **将数据库连接为数据源(datasource)。** 数据源是一个具名连接 —— + Postgres、MySQL、MongoDB、SQLite。凭据来自环境变量,如果你想先做分析, + 这个连接也可以是只读的。 + + ```ts + import type { Datasource } from '@objectstack/spec'; + + export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + active: true, + }; + ``` + +2. **将数据表建模为对象** —— 而且你不必手工敲出它们。把像 Claude Code 这样的 + 编码 Agent 指向已连接的 schema,让它为每张表生成一个源级别的对象文件。我们的 + [`hotcrm`](https://github.com/objectstack-ai/hotcrm) 参考应用展示了输出的确切 + 形态:一个 `ObjectSchema.create({ … })`,带有类型化的 `Field.*` 定义,外键则用 + `Field.lookup(...)`。你来审阅和打磨;结果归你所有。 + +3. **将对象绑定到数据源** —— 可以逐个对象绑定,也可以用一条路由规则绑定整个 + 命名空间: + + ```ts + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ] + ``` + +4. **让 AI 开始工作。** 一旦一张表成为对象,AI 层就能免费地在它上面工作。Agent + 通过 ObjectQL 查询,由它将每个对象路由到对应的数据库 —— 而且每一次查询都以 + **已登录用户**的身份运行,遵守对象级、记录级和字段级的权限。 + +## 为什么这是安全的 + +对"在我们的业务数据上跑 AI"的质疑总是关于治理,而这恰恰是运行时发挥作用的地方: + +- **AI 以用户身份行动,绝不凌驾其上。** Agent 看到的,恰好是它背后那个人被允许 + 看到的内容 —— 由运行时强制,而非由提示词约束。 +- **如果你愿意,默认只读。** 把对象绑定到一个只读连接或只读数据库用户。安全地 + 分析生产数据;再有意识地开启写入。 +- **一切皆有审计。** 每一次读取、写入和提权 —— 无论来自人还是 Agent —— + 都记录下何人、何事、何时、何因。 +- **你的数据留在你的网络里。** ObjectOS 跑在你的环境中。业务数据和提示词从不 + 离开你的边界。 + +## 已经交付的 vs. 即将到来的 + +上面这套流程**今天**就能用,凭借的是已交付的构建块:数据源、按对象路由、 +感知能力的 ObjectQL 下推(pushdown),以及 AI Agent 层。对象的生成由一个编码 +Agent 针对你已连接的 schema 完成 —— 与 `hotcrm` 的对象被编写出来的方式相同。 + +一套更丰富的**开箱即用联邦(turn-key federation)**体验 —— 一步式 schema 导入、 +绑定到外部拥有的 schema、内建的写入安全闸门 —— 正在 +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +下积极设计(状态:*Proposed*)。随着它落地,我们会写更多内容。在那之前, +有文档记载的路径才是受支持的路径。 + +## 试一试 + +- [数据源](/docs/configure/data-sources) —— 完整的编写指南 +- [扩展现有系统](/docs/extend-existing-systems) —— 端到端的场景 +- [AI Agent](/docs/build/agents) —— 在你的对象之上声明式地构建 Agent + +如果你已经有一个业务数据库,那你已经走完了大半路程。连上它,建模几张表, +然后向你的数据提个问题。 diff --git a/content/docs/architecture.ko.mdx b/content/docs/architecture.ko.mdx new file mode 100644 index 0000000..6d05d41 --- /dev/null +++ b/content/docs/architecture.ko.mdx @@ -0,0 +1,139 @@ +--- +title: 아키텍처 +description: 실제로 무엇이 실행되는가 — 이 제품을 도입할지 평가하는 엔지니어를 위한 안내. +--- + +# 아키텍처 + +ObjectOS를 배포할 때 여러분의 머신에서 무엇이 실행되는지, 어떤 데이터가 +네트워크 밖으로 나가고 어떤 데이터가 나가지 않는지에 대한 실용적인 관점입니다. + +핵심 개념은 두 개의 얇은 계층입니다. + +1. **메타데이터** — 객체 / 뷰 / 액션 / 플로우 / 에이전트의 패키지입니다. + 대부분은 샌드박스화된 도구 API를 대상으로 [AI Builder](/docs/build/ai-builder)가 + 작성하며, 때때로 직접 편집되기도 하지만 항상 버전 관리되고 감사됩니다. +2. **단일 Node.js 런타임** — 이 메타데이터를 동작하는 애플리케이션으로 + 해석합니다. REST API, Console UI, 권한, 작업, AI 도구가 모두 하나의 + 프로세스 안에서 여러분의 데이터베이스와 통신합니다. + +코드 생성 단계도, "사용자가 원하는 것을 설명함"과 "그것이 라이브로 동작함" +사이의 배포 파이프라인도 없습니다. 런타임은 HITL 승인 이후 새 메타데이터를 +핫 로드합니다. + +## 무엇을 배포하는가 + +ObjectOS 인스턴스당 하나의 Node.js 프로세스. 그것이 전부입니다. + +```text +┌─────────────────────────────────────────────────────┐ +│ ObjectOS process │ +│ ┌───────────────────────────────────────────────┐ │ +│ │ HTTP dispatcher (/ · /api · /_console …) │ │ +│ ├───────────────────────────────────────────────┤ │ +│ │ Per-project ObjectKernel (LRU cached) │ │ +│ │ ├─ Auth (Better Auth) │ │ +│ │ ├─ Security (RBAC + row-level + field) │ │ +│ │ ├─ ObjectQL (data engine, generates SQL) │ │ +│ │ ├─ REST API generator │ │ +│ │ └─ Capabilities loaded per artifact │ │ +│ │ (audit, storage, jobs, queue, AI …) │ │ +│ └───────────────────────────────────────────────┘ │ +└──────────┬──────────────────────────────────────────┘ + │ + ▼ + Your business database + (Postgres / MySQL / SQLite / Turso / MongoDB) +``` + +정적으로 링크된 단일 바이너리 수준의 복잡도입니다. 사이드카도, Kafka도, +별도의 캐시 계층도 필요하지 않습니다. 필요할 때 추가하세요. 첫날부터 그 +비용을 치를 필요는 없습니다. + +## 데이터는 어디에 있는가 + +| 데이터 | 저장 위치 | 네트워크 밖으로 나가는가? | +|---|---|---| +| 비즈니스 레코드 | 여러분의 데이터베이스 | **아니오** | +| 사용자 계정, 세션, OAuth 토큰 | 여러분의 데이터베이스 | **아니오** | +| 감사 로그 | 여러분의 데이터베이스 | **아니오** | +| 설정, API 키, 시크릿 | 여러분의 데이터베이스 / 시크릿 매니저 | **아니오** | +| 업로드된 파일 | 여러분의 디스크 또는 S3/R2 버킷 | **아니오** | +| 컴파일된 앱 정의(`objectstack.json`) | 디스크의 파일 또는 컨트롤 플레인에서 가져옴 | 선택적 | + +ObjectOS는 외부로 연결을 시도하지 않습니다. 텔레메트리도, 라이선스 검사도 +없습니다. 인터넷 접속을 완전히 차단해도 무기한 계속 실행됩니다. +[에어갭](/docs/deploy/air-gapped)을 참고하세요. + +## 요청이 처리되는 방식 + +```text +1. Ingress / TLS termination (your load balancer) +2. HTTP dispatcher (security headers, request id) +3. Hostname → project resolution (cached, TTL configurable) +4. Get or build per-project kernel from LRU +5. AuthPlugin — session cookie, bearer token, or API key +6. SecurityPlugin — RBAC + row-level + field-level checks +7. Route handler — generated REST, declarative action, or custom +8. Data driver — ObjectQL compiles to SQL / Mongo query +9. Response with X-Request-Id propagated +``` + +커널이 워밍업된 상태에서는 4~8단계가 일반적으로 5ms 미만으로 실행됩니다. + +## 세 개의 계층 (통합할 때만 중요함) + +대부분의 고객은 **ObjectOS**만 배포합니다. 나머지 두 계층은 아티팩트가 +어디에서 오는지 알고 싶을 때를 위해 존재합니다. + +| 계층 | 무엇인가 | 어디에서 실행되는가 | +|---|---|---| +| **Framework**(`@objectstack/*`) | 오픈소스 커널, ObjectQL, 플러그인, 드라이버 | npm — 빌드 시점에 포함됨 | +| **Control plane**(선택적) | 컴파일된 `objectstack.json` 아티팩트를 게시함. 호스팅되는 ObjectStack Cloud를 사용하거나, 직접 운영하거나, 완전히 생략할 수 있음 | 여러분의 CI, 우리 클라우드, 또는 여러분의 노트북 | +| **ObjectOS** | 여러분이 운영하는 런타임 | **여러분의 인프라** | + +단일 앱을 출시한다면 컨트롤 플레인이 필요하지 않습니다. CI에서 +`objectstack.config.ts → dist/objectstack.json`을 컴파일하고 그 JSON을 +이미지에 담아 출시하세요. 다수의 테넌트와 앱을 가진 내부 앱 마켓플레이스를 +운영한다면, 컨트롤 플레인이 카탈로그가 머무는 곳입니다. + +## 부팅 모드 + +| 모드 | 언제 | 어떻게 | +|---|---|---| +| **Standalone** | 단일 앱, 개발, 평가, 에어갭, 대부분의 프로덕션 배포 | `pnpm dev` 또는 디스크에서 `dist/objectstack.json` 실행 | +| **File-backed** | 외부에서 관리되는 아티팩트를 사용하는 프로덕션 | `OS_ARTIFACT_PATH=/path/to/objectstack.json` 설정 | +| **Cloud-connected** | 컨트롤 플레인이 공급하는 멀티 테넌트 / 멀티 앱 배포 | `OS_CLOUD_URL` + `OS_CLOUD_API_KEY` 설정 | + +모드는 환경 변수로부터 자동 감지됩니다. + +## 성능 특성 + +| 지표 | 수치 | +|---|---| +| 콜드 스타트(프로세스 기동, 트래픽 수신 준비) | 약 1초 | +| 커널당 워밍업(프로젝트로의 첫 요청) | 기능에 따라 50~300ms | +| 워밍업된 요청 지연(REST를 통한 CRUD) | 일반적으로 10ms 미만 + 데이터베이스 지연 | +| 메모리 사용량 | 기본 약 150MB, 활성 프로젝트 커널당 약 10~30MB | +| 인스턴스당 동시 프로젝트 수 | `OS_KERNEL_CACHE_SIZE`로 제한됨(기본값 32) | + +## 왜 이런 형태인가 + +- **사이드카 없는 단일 Node 프로세스** → `docker run`에 들어맞고, systemd + 유닛에 들어맞고, Lambda 같은 환경에 들어맞습니다. +- **프로젝트당 커널, LRU 캐시** → 하나의 인스턴스가 매 요청마다 워밍업 + 비용을 치르지 않고도 많은 소규모 앱을 처리할 수 있습니다. +- **선언된 메타데이터 위에 생성되는 API** → CI에 코드 생성 단계가 없고, + 게시할 클라이언트 SDK도 없습니다. API는 구조적으로 여러분의 데이터 + 모델과 일치합니다. +- **모든 기능은 선택적 플러그인** → 이미지 크기가 실제로 사용하는 것에 + 맞춰 조정됩니다. + +## 다음으로 갈 곳 + +- [프로덕션 준비성](/docs/operate/production) — 실제 트래픽에 노출하기 전의 + 체크리스트. +- [런타임 구성](/docs/configure/runtime) — 데이터베이스, 캐시, 시크릿 + 연결하기. +- [런타임 기능](/docs/reference/runtime-capabilities) — 어떤 선택적 + 패키지가 존재하고 무엇을 활성화하는지. diff --git a/content/docs/build/actions.ko.mdx b/content/docs/build/actions.ko.mdx new file mode 100644 index 0000000..cc34bd0 --- /dev/null +++ b/content/docs/build/actions.ko.mdx @@ -0,0 +1,201 @@ +--- +title: 액션(Actions) +description: 플랫폼이 REST 엔드포인트, Console 버튼, 플로우 단계, AI 도구로 노출하는 명명된 작업 — 단 하나의 선언으로 제공됩니다. +--- + +# 액션(Actions) + +**액션(Action)**은 객체에 대한 명명된 작업입니다. 한 번만 선언하면 +다음과 같이 나타납니다: + +- `/api/v1/actions//` 위치의 **REST 엔드포인트** +- Console 레코드 상세 화면의 **버튼** +- 자동화를 위한 **플로우 단계**(`type: 'action'`) +- Agents와 AI Builder를 위한 **AI 도구**(`action_`) + +네 가지 표면에 걸쳐 같은 작업을 반복하지 않습니다. 하나의 선언으로 네 가지 +호출 방법을 제공합니다. + +## 액션 선언하기 + +```ts +// src/actions/approve_invoice.action.ts +import { Action } from '@objectstack/spec'; + +export const approveInvoice = Action.create({ + name: 'approve_invoice', // lowercase snake_case (machine id) + label: 'Approve Invoice', + objectName: 'invoice', // attaches to the invoice object + icon: 'check', + variant: 'primary', + locations: ['record_header'], // where the button shows + confirmText: 'Approve this invoice?', + successMessage: 'Invoice approved', + refreshAfter: true, + + // collect input before running + params: [ + { name: 'note', label: 'Approval note', type: 'textarea' }, + ], + + // only show the button when the record is still pending + visible: 'record.status == "pending"', + + // what it does — a sandboxed script body + type: 'script', + body: { + language: 'js', + source: ` + await ctx.data.update('invoice', input.id, { + status: 'approved', + approved_by: ctx.user.id, + approved_at: now(), + approval_note: input.note, + }); + `, + }, +}); +``` + +`os dev`가 재컴파일한 후: + +- `POST /api/v1/actions/invoice/approve_invoice`가 동작합니다 +- Console의 Invoice 레코드 페이지에 **Approve Invoice** 버튼이 표시됩니다 +- 플로우에 `{ type: 'action', action: 'approve_invoice', inputs: { note: '…' } }`를 포함할 수 있습니다 +- 스킬이 허용하는 경우 AI 어시스턴트가 `action_approve_invoice`를 호출할 수 있습니다 + +## 액션 유형 + +`type` 필드가 액션이 수행할 작업을 결정합니다: + +| `type` | 실행되는 것 | 사용 목적 | +|---|---|---| +| `script` | `body` — L1 formula 표현식 또는 샌드박스화된 L2 JavaScript | 대부분의 경우 — 서버 측 로직, 감사 가능 + AI 호출 가능 | +| `api` | `target` 엔드포인트로의 HTTP 호출(`method`, `bodyExtra`) | 데이터 API 또는 플랫폼 엔드포인트 재사용 | +| `flow` | `target`에 명명된 플로우를 실행 | 다단계 비즈니스 프로세스 | +| `url` | `target` URL로 이동 | 딥 링크, 리디렉션 방식의 액션 | +| `modal` | `target`에 명명된 페이지/모달을 열기 | 사용자 정의 대화 상자 | +| `form` | `target`에 명명된 FormView를 열기 | 가이드 방식의 데이터 입력 | + +```ts +// api type — reuse a data-API endpoint +Action.create({ + name: 'archive_order', + objectName: 'order', + label: 'Archive', + locations: ['list_item'], + type: 'api', + method: 'PATCH', + target: '/api/v1/data/order/{id}', + bodyExtra: { archived: true }, +}); +``` + +`script`이 아닌 유형은 `target`이 필요합니다. 어떤 유형이든 액션은 모든 +표면에서 동일한 일급 시민입니다. + +## 액션 호출하기 + +### REST + +```bash +# the record id can go in the body, or in the path +curl -X POST https://app.example.com/api/v1/actions/invoice/approve_invoice/inv_123 \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + -d '{"note": "LGTM"}' +``` + +파라미터는 요청 본문에 평면 형태로 전송됩니다. 레코드 id는 후행 경로 세그먼트 +(`.../approve_invoice/:recordId`)로 제공하거나 본문에 포함할 수 있습니다. 응답은 +스크립트 본문의 반환 값(또는 `api` 유형 액션의 경우 호출 결과)입니다. + +### Console + +기본적으로 Console은 액션의 `visible` 조건으로 필터링하여 레코드 상세 페이지에 +버튼으로 액션을 표시합니다. 뷰 설정에서 배치를 재정의할 수 있습니다: + +```ts +defineView({ + name: 'invoice_detail', + object: 'invoice', + actions: ['approve_invoice', 'reject_invoice', 'send_to_customer'], +}); +``` + +### 플로우에서 + +```ts +{ + type: 'action', + action: 'approve_invoice', + inputs: { note: 'Auto-approved by SLA flow' }, + record: '{!trigger.record.id}', +} +``` + +### AI Agent에서 + +`approve_invoice`가 에이전트가 보유한 스킬에 포함되어 있으면 LLM이 이를 +호출할 수 있습니다. 입력은 대화에서 가져오며, 권한은 사용자가 직접 호출한 +것처럼 적용됩니다. + +> *"인보이스 INV-2042를 '전화로 확인됨'이라는 메모와 함께 승인해줘."* + +## 권한 + +액션은 호출하는 사용자의 권한으로 실행됩니다. 플랫폼은 다음을 확인합니다: + +1. **객체 권한** — 사용자의 [권한 세트](/docs/configure/permissions/permission-sets)가 + 액션에 필요한 객체 수준 접근 권한(예: 업데이트)을 부여해야 합니다. +2. **필드 권한** — 액션이 쓰는 모든 필드에 대해 사용자가 쓰기 접근 권한(FLS)을 + 가지고 있어야 합니다. +3. **UI 게이팅** — `visible` 및 `disabled` 조건(CEL, `record`, `os.user`, + 파라미터에 대해 평가됨)이 Console에서 버튼이 렌더링될지 또는 회색 처리될지를 + 제어합니다. + +권한 확인에 실패하면 `PERMISSION_DENIED` 오류와 함께 `403`을 반환합니다. + +## 내장 액션 + +모든 객체는 다음을 기본으로 제공받습니다: + +| 액션 | 수행하는 작업 | +|---|---| +| `create` | 레코드 삽입 | +| `update` | 레코드 업데이트 | +| `delete` | 레코드 삭제(또는 소프트 삭제) | +| `restore` | 소프트 삭제 취소 | +| `clone` | 레코드 깊은 복사 | +| `share` | 사용자 / 역할과 직접 공유 | + +이러한 액션은 다시 선언하지 마세요 — 객체의 [라이프사이클 및 기능 플래그](/docs/build/data-model)를 따릅니다. + +## 감사(Auditing) + +플랫폼 이벤트는 다음 필드를 포함하는 불변 기록인 `sys_audit_log`에 기록됩니다: + +- `user_id` — 작업을 시작한 사용자 +- `action` — 액션 이름 +- `object_name` 및 `record_id` — 변경된 대상 +- `old_value` / `new_value` — 변경 내용 +- `ip_address` / `user_agent` — 요청 출처 +- `created_at` — 발생 시점 + +이는 *"누가 버튼을 눌렀는가?"*라는 질문에 가장 먼저 확인할 곳입니다. + +## AI Builder로 액션 생성하기 + +> *"`support_ticket`에 우선순위를 긴급으로 설정하고 온콜 엔지니어에게 +> 할당하는 액션 `escalate_ticket`을 만들어줘."* + +[AI Builder](/docs/build/ai-builder)는 액션 메타데이터를 생성하고 변경 사항을 +승인 대기열에 넣습니다. 승인 후에는 REST, Console, 플로우, 그리고 — 재귀적으로 — +AI 자체에서 액션을 호출할 수 있습니다. + +## 다음으로 갈 곳 + +- [플로우](/docs/build/flows) — 여러 액션을 비즈니스 로직으로 구성하기 +- [Agents](/docs/build/agents) — 액션을 AI 도구로 노출하기 +- [API 접근](/docs/configure/api-access) — 외부 시스템에서 액션 호출하기 +- [권한](/docs/configure/permissions) — 누가 무엇을 호출할 수 있는지 제어하기 diff --git a/content/docs/build/agents.ko.mdx b/content/docs/build/agents.ko.mdx new file mode 100644 index 0000000..248ac57 --- /dev/null +++ b/content/docs/build/agents.ko.mdx @@ -0,0 +1,186 @@ +--- +title: Agents +description: 엔드 유저용 AI 어시스턴트 — Agent → Skill → Tool — 데이터와 액션으로 연결됩니다. +--- + +# Agents + +Agent는 **엔드 유저**가 대화하는 AI 어시스턴트입니다 — 헬프데스크 +부조종사, 영업 BDR, 사내 HR Q&A 봇 등이 그 예입니다. 이들은 여러분이 +이미 정의해 둔 데이터와 액션 위에서 동작합니다. 새 코드를 작성하는 것이 +아니라, 기존 기본 요소들을 하나의 페르소나로 조합하는 것입니다. + +Salesforce Agentforce, Microsoft Copilot Studio, ServiceNow Now Assist와 +같은 3계층 아키텍처입니다: + +```text +Agent ──→ Skill ──→ Tool +(persona) (capability) (callable function) +``` + +| 계층 | 무엇인지 | 예시 | +|---|---|---| +| **Tool** | 호출 가능한 단일 함수 (액션, 쿼리, 지식 검색, MCP 메서드) | `create_ticket`, `get_order_status`, `search_kb` | +| **Skill** | 공유 LLM 지침을 가진, 관련 도구들의 명명된 묶음 | `ticket_management` = create + update + close + escalate | +| **Agent** | 역할, 시스템 프롬프트, 연결된 스킬, 지식을 가진 페르소나 | `tier1_support` = 공감적이고, 신원을 확인하며, `ticket_management` + `kb_search`를 보유 | + +## Agent 정의하기 (파일 하나) + +```ts +// src/agents/tier1_support.agent.ts +import { defineAgent } from '@objectstack/spec/ai'; + +export const tier1Support = defineAgent({ + name: 'tier1_support', + label: 'First Line Support', + role: 'Help Desk Assistant', + instructions: ` + You are a friendly first-line support agent. + Always verify the user's identity before discussing account specifics. + Escalate to tier 2 if the issue involves billing or security. + `, + skills: ['ticket_management', 'knowledge_search'], + knowledge: { + topics: ['faq', 'policies'], + indexes: ['support_docs'], + }, + model: { provider: 'openai', model: 'gpt-4o', temperature: 0.3 }, + memory: { shortTerm: { maxMessages: 30 } }, +}); +``` + +또는 Console에서: **Console → Agents → New Agent**. + +또는 — 그리고 이것이 핵심입니다 — AI Builder에게 이렇게 말하세요: + +> *"티켓 관리를 처리하고 FAQ를 검색하는 1차 지원 에이전트를 만들어 줘. +> 계정 세부 정보를 논의하기 전에 신원을 확인해야 해."* + +## Skill 정의하기 + +```ts +// src/skills/ticket_management.skill.ts +import { defineSkill } from '@objectstack/spec/ai'; + +export const ticketManagement = defineSkill({ + name: 'ticket_management', + label: 'Ticket Management', + instructions: ` + Always confirm the ticket subject and priority before creating one. + Use 'urgent' priority sparingly — only for outages or security incidents. + `, + tools: [ + 'create_ticket', + 'update_ticket', + 'close_ticket', + 'escalate_ticket', + 'action_*', // wildcard: pick up any future actions on the active object + ], +}); +``` + +Skill은 **재사용에 적합한 단위**입니다. 하나의 스킬이 여러 에이전트에 +걸쳐 동작합니다. + +## Tool은 선언한 메타데이터에서 나옵니다 + +여러분이 선언하는 모든 `*.action.ts`는 별도의 연결 작업 없이 자동으로 +`action_` 도구로 구체화됩니다. 따라서 `support_ticket` 객체에 +`escalate_ticket`을 Action으로 이미 정의해 두었다면, AI Builder와 +여러분의 Agent 모두가 이를 호출할 수 있습니다. 권한은 여전히 +적용됩니다: 에이전트는 **사용자 자격으로** 액션을 호출하므로, 사용자의 +권한 집합이 성공 여부를 결정합니다. + +다음도 노출할 수 있습니다: + +| 도구 유형 | 출처 | +|---|---| +| **Action** | 설치된 모든 패키지의 모든 `*.action.ts` | +| **Flow** | 모든 수동 플로우 (`type: 'manual'`) | +| **Query** | 저장된 ObjectQL 쿼리 (`*.query.ts`) | +| **Knowledge search** | 에이전트에 연결된 모든 지식 인덱스 | +| **MCP method** | 연결된 MCP 서버가 노출하는 모든 것 | +| **Built-in metadata tools** | `create_object`, `add_field`, … — 단, 관리자 에이전트에만 한정 | + +## 앰비언트 어시스턴트 패턴 + +사용자가 에이전트를 직접 고르게 강제하는 대신 **앱 전체에 하나의 채팅 +박스**(Claude Code / Agentforce 스타일)를 두고 싶다면, App 메타데이터에 +`defaultAgent`를 선언하고 앱 컨텍스트와 함께 앰비언트 채팅 엔드포인트를 +호출하세요: + +```text +POST /api/v1/ai/chat { context: { appName: 'crm' }, ... } +``` + +`context.appName`이 `defaultAgent`를 선언한 앱으로 해석되면, 런타임이 +해당 에이전트를 자동으로 선택합니다 — 사용자는 목록에서 고를 필요가 +없습니다. Console의 내장 AI 패널이 이 방식을 사용합니다. 런타임은 다음을 +해석합니다: + +1. 활성 앱의 **기본 에이전트**(앱의 `defaultAgent`), 또는 사용자가 접근할 + 수 있는 첫 번째 에이전트. +2. **활성 스킬** — Skill Registry에서 로드된 에이전트의 `skills:` 목록으로, + 사용자 권한 집합과 현재 객체/레코드 컨텍스트로 필터링됩니다. +3. 에이전트에 연결된 **지식**. + +어떤 에이전트를 어디에 표시할지 일일이 연결할 필요가 없습니다. 앱을 +선언하고 그 `defaultAgent`를 설정하면 나타납니다. + +## 권한 + +| 기능 | 권한 | +|---|---| +| 에이전트와 대화 | `ai:chat` (그리고 에이전트의 스킬 도구에 대한 접근) | +| 메타데이터 변경 승인 | `ai:approve` | +| 에이전트 및 스킬 정의/편집 | `ai:author` (일반적으로 Setup Administrator) | +| AI 대화 읽기 (감사) | `ai:read` | + +대화는 사용자 단위로 범위가 한정됩니다 — 위임 권한이 부여되지 않는 한 +한 사용자는 다른 사용자의 채팅 기록을 볼 수 없습니다. + +## 메모리와 대화 상태 + +에이전트의 `memory` 블록에는 두 개의 계층이 있습니다: + +| 필드 | 역할 | +|---|---| +| `shortTerm.maxMessages` | 작업 메모리에 유지되는 최근 메시지 (기본값 `50`) | +| `shortTerm.maxTokens` | 단기 컨텍스트 윈도우를 위한 선택적 토큰 예산 | +| `longTerm.enabled` | 세션 간 메모리 유지 (기본값 `false`) | +| `longTerm.store` | 유지되는 메모리의 백엔드: `vector` (기본값), `database`, 또는 `redis` | +| `reflectionInterval` | N회 상호작용마다 반영하여 동작을 정교화 | + +라이브 컨텍스트 윈도우의 가지치기는 대화의 토큰 예산 전략에 의해 +좌우됩니다 — `sliding_window` (기본값), `fifo`, `importance`, `semantic`, +또는 `summary`. + +대화 행은 `ai_conversations`에 저장됩니다. 도구 호출 결과와 대기 중인 +액션은 감사를 위해 출발점이 된 대화를 상호 참조합니다. + +## 관찰 가능성 + +모든 에이전트 실행은 다음을 발생시킵니다: + +- `audit:ai:chat` 이벤트 (턴당) +- `audit:ai:tool` 이벤트 (도구 호출당, 입력 + 출력 포함) +- `audit:ai:pending_action` 이벤트 (변경이 큐에 들어갈 때) +- 토큰 카운트 지표 (모델별, 프로바이더별)를 청구 분담을 위해 감사 로그에 + 기록 + +이것들을 평소 사용하는 관찰 가능성 스택에 연결할 수 있습니다 — +[Observability](/docs/operate/observability)를 참조하세요. + +## 멀티테넌트 참고 사항 + +Agent는 환경(Environment)별로 존재합니다. 동일한 에이전트 정의를 +marketplace 패키지로 배포하더라도, 테넌트 A의 `tier1_support` 에이전트는 +테넌트 B의 데이터, 대화, 지식을 결코 보지 못합니다. + +## 다음으로 갈 곳 + +- [AI Builder](/docs/build/ai-builder) — 빌드 타임 어시스턴트 +- [IDE Skills](/docs/build/ai-skills) — `npx skills add objectstack-ai/framework` 로 IDE 에이전트가 메타데이터를 올바르게 작성하도록 합니다 +- [Actions](/docs/build/actions) — 에이전트가 사용할 도구를 선언합니다 +- [AI Service](/docs/configure/ai) — 프로바이더, 임베더, MCP 설정 +- [`@objectstack/spec/ai`](https://github.com/objectstack-ai/framework/tree/main/packages/spec/src/ai) — 전체 스키마 diff --git a/content/docs/build/ai-builder.ko.mdx b/content/docs/build/ai-builder.ko.mdx new file mode 100644 index 0000000..35c025a --- /dev/null +++ b/content/docs/build/ai-builder.ko.mdx @@ -0,0 +1,183 @@ +--- +title: AI Builder +description: 일상 언어로 작성한 요구사항을 실행 가능한 메타데이터로 바꿔주는 Console 내장 채팅. +--- + +# AI Builder + +AI Builder는 **고객이 ObjectOS를 확장하는 기본 방법**입니다. Console을 +열고 어시스턴트에게 일상 언어로 말을 걸면, 패키지, 객체, 필드, 액션, +플로우 등 메타데이터를 대신 만들어 줍니다. 모든 변경은 실제로 적용되기 +전에 Human-in-the-Loop(HITL) 승인 목록에 대기열로 들어갑니다. + +## 30초 만에 사용해 보기 + +``` +You: I need to track customer support tickets. + Each ticket has a subject, description, priority (low/medium/high/urgent), + status (new/open/pending/resolved/closed), and assignee. + +AI: I'll create that for you. Here's the plan: + • Create package `com.you.support` (v0.1.0) + • Create object `support_ticket` with 5 fields + • Add a kanban view grouped by status + • Add 3 permission sets: agent, manager, viewer + + Approve? [Yes] [Modify] [Cancel] + +You: [Yes] + +AI: ✓ Package created + ✓ Object created with 5 fields and 12 indexes + ✓ Kanban view registered + ✓ Permission sets created + Done. Try it: /support_ticket +``` + +이제 동작하는 티켓 관리 앱이 생겼습니다. REST 엔드포인트, Console 뷰, +감사 로그 항목, 권한 체크포인트까지 전부 갖춰져 있습니다. 파일을 편집하지도, +재시작하지도 않았습니다. + +## AI가 할 수 있는 일 + +어시스턴트는 모두 네임스페이스로 구분되고 `@objectstack/spec`에 대해 +검증되는 **내장 메타데이터 도구** 모음에 접근할 수 있습니다. + +| 분류 | 도구 | 하는 일 | +|---|---|---| +| **Packages** | `create_package` | 매니페스트 + 버전을 포함한 새 컨테이너 | +| | `list_packages` | 기존 패키지 둘러보기 | +| | `get_package` / `get_active_package` | 내용 확인 | +| | `set_active_package` | 대화에서 작업할 패키지 선택 | +| **Objects** | `create_object` | 초기 필드를 포함한 새 객체 | +| | `list_objects` | 활성 패키지의 객체 둘러보기 | +| | `describe_object` | 스키마, 필드, 관계 출력 | +| **Fields** | `add_field` | 타입이 지정된 필드 추가 (일반 [필드 타입](/docs/reference/field-types)) | +| | `modify_field` | 레이블, 선택 목록 옵션, 검증 변경 | +| | `delete_field` | 필드 제거 (안전 검사 포함) | +| **Data** | `query_data` | ObjectQL로 레코드 읽기 | +| **Actions / Knowledge** | `action_`, `search_knowledge` | 액션 호출, 문서에 대한 RAG | + +여기에 더해, 선언된 모든 `*.action.ts`로부터 만들어진 액션 도구가 +`action_` 형태로 제공됩니다. 따라서 패키지에 액션이 존재하기만 하면 +AI는 그것을 다른 내장 도구처럼 호출할 수 있습니다. + +런타임에서 전체 도구 카탈로그 확인: `GET /api/v1/ai/tools`. + +## Human-in-the-Loop 승인 + +**메타데이터를 변경하는** 도구 호출은 대기 중인 액션 대기열을 거칩니다. +승인 담당자는 *Approve*를 클릭하기 전에 제안된 변경 사항의 diff를 확인합니다. + +| 엔드포인트 | 목적 | +|---|---| +| `GET /api/v1/ai/pending-actions` | 대기 중인 액션 목록 (상태로 필터링) | +| `GET /api/v1/ai/pending-actions/:id` | 제안된 단일 액션의 diff | +| `POST /api/v1/ai/pending-actions/:id/approve` | 변경 적용 | +| `POST /api/v1/ai/pending-actions/:id/reject` | 사유와 함께 폐기 | + +필요한 권한: + +| 작업 | 권한 | +|---|---| +| 대기열 읽기 | `ai:read` | +| 승인 / 거부 | `ai:approve` | + +기본값으로는 **Setup Administrator** 멤버만 `ai:approve` 권한을 가집니다. +[Permission Sets](/docs/configure/permissions/permission-sets)에서 더 세분화할 수 +있습니다. 예를 들어 "프로덕트 오너는 `com.acme.crm`에서 승인할 수 있고, 그 외에는 +아무도 할 수 없다"와 같이 지정할 수 있습니다. + +이 대기열은 감사가 가능합니다. 모든 승인/거부는 발신 대화 id와 함께 +`sys_audit_log`에 기록됩니다. + +## 대화와 컨텍스트 + +각 채팅은 하나의 `ai_conversations` 레코드입니다. 플랫폼은 다음을 추적합니다. + +- **활성 패키지** — `set_active_package`로 설정됩니다. 새 객체 / 필드가 + 여기에 생성됩니다. +- **Skills** — 현재 컨텍스트에서 사용할 수 있는 도구의 부분 집합입니다 + (예: `support_ticket` 상세 페이지 내부에서는 AI가 + `support_ticket` 범위의 스킬을 사용할 수 있습니다). +- **Knowledge** — 대화에 연결된 RAG 주제 + 인덱스입니다. + +```text +POST /api/v1/ai/assistant/chat +{ + "messages": [{ "role": "user", "content": "Add a tags field to support_ticket" }], + "context": { "objectName": "support_ticket", "recordId": "tkt_123" } +} +``` + +플랫폼은 활성 애플리케이션의 기본 Agent를 확인하고, 컨텍스트에 따라 +스킬 집합을 필터링한 다음, LLM이 어떤 도구를 호출할지 선택하도록 합니다. +에이전트를 특정 UI에 미리 연결할 필요가 없습니다. 에이전트는 메타데이터를 +기반으로 스스로 연결됩니다. + +## 요청할 수 있는 것들 + +검증된 패턴과 한 줄 프롬프트입니다. + +| 목표 | 프롬프트 | +|---|---| +| 새 객체 | *"Create a `product` object with name, sku, price, category."* | +| 필드 추가 | *"Add a `discount` decimal field to `order`, max 50."* | +| 선택 목록 변경 | *"On `task.status`, add 'blocked' between 'open' and 'done'."* | +| 인덱스 | *"Index `order` by status + created_at for the dashboard."* | +| 관계 | *"Make `order_line` master-detail to `order`."* | +| 검증 | *"On `invoice`, the discount can't exceed the subtotal."* | +| 플로우 | *"When a high-priority ticket sits in 'new' for 30 minutes, notify the manager."* | +| 액션 | *"Create an action `approve_invoice` that sets status to approved."* | +| 권한 세트 | *"Create `agent` permission set with read/create/edit on support_ticket and read on customer."* | +| 번역 | *"Translate the support_ticket labels and picklists to Spanish."* | + +AI가 처리할 수 없는 경우에는 그렇다고 알려주고, 수동 경로(보통 30줄짜리 +`*.ts` 파일이나 Console 폼)를 안내해 줍니다. + +## 실시간 미리보기 + +승인된 변경이 적용될 때마다 Console은 영향을 받은 뷰를 그 자리에서 다시 +렌더링합니다. 새로고침이 필요 없습니다. 커널 캐시는 변경된 패키지를 +무효화하므로, 이후 요청은 새 메타데이터를 사용합니다. + +## 롤백 + +대기 중인 액션 레코드는 **변경 이전 상태**를 보관합니다. 변경이 잘못된 +경우, 진행 중인 항목을 거부하고 AI에게 요청하세요. + +``` +You: Roll back the last 3 changes to support_ticket. +AI: Found 3 mutations from conversation conv_abc (3 minutes ago). + Restoring 'support_ticket' to its state at 13:42:11. + Approve? [Yes] +``` + +더 큰 규모의 롤백에는 마지막으로 정상 동작하던 아티팩트에 대해 +[`os diff`](/docs/reference/cli)를 사용하고 역방향 diff를 적용하세요. + +## 한계 (현재 기준) + +- **데이터 접근은 운영자 본인의 권한을 따릅니다.** 어시스턴트는 로그인한 + 사용자로 실행되므로, 해당 사용자가 이미 접근할 수 있는 레코드만 읽거나 + 쓸 수 있습니다. 대부분의 팀은 어시스턴트를 메타데이터에 집중시키고, + 고객 데이터에 대해서는 변경이 아니라 조회만 하도록 둡니다. +- **패키지 간 변경에는 명시적인 확인이 필요합니다.** AI에게 시스템 패키지 + (`sys_*`)를 수정하도록 요청하면 거부 메시지가 반환됩니다. 플랫폼은 + 원칙적으로 이를 거부합니다. +- **장시간 실행되는 다단계 계획**(예: "HR 앱을 처음부터 전부 구축")도 + 동작하지만, 한두 개의 객체씩 다루는 여러 개의 작은 대화로 나누어 + 진행하는 것이 가장 좋습니다. + +## 빌드 타임 AI를 넘어서 + +동일한 AI 인프라가 최종 사용자를 위한 **런타임 에이전트**(지원 +어시스턴트, 영업 코파일럿, 사내 Q&A 봇 등)를 구동하며, 이들은 모두 동일한 +Agent → Skill → Tool 아키텍처 위에 구축됩니다. [Agents](/docs/build/agents)를 참고하세요. + +## 다음으로 갈 곳 + +- [Agents](/docs/build/agents) — 데이터 위에 구축하는 최종 사용자용 어시스턴트 +- [Data Model](/docs/build/data-model) — AI가 실제로 생성하는 것 +- [Actions](/docs/build/actions) — AI가 런타임 도구로 노출하는 단위 +- [AI Service](/docs/configure/ai) — 프로바이더 설정 (OpenAI / Anthropic / Doubao / …) diff --git a/content/docs/build/ai-skills.ko.mdx b/content/docs/build/ai-skills.ko.mdx new file mode 100644 index 0000000..3d4be33 --- /dev/null +++ b/content/docs/build/ai-skills.ko.mdx @@ -0,0 +1,115 @@ +--- +title: IDE Skills (Claude Code / Cursor / Copilot) +description: 코딩 에이전트에 ObjectOS skills를 설치하여 Claude Code, Cursor, Copilot, Codex 등이 ObjectOS 메타데이터를 올바르게 작성하는 방법을 알도록 하세요. +--- + +# IDE Skills + +[AI Builder](./ai-builder)는 Console 안에서 동작하며 테넌트의 +데이터베이스와 통신합니다. 하지만 때로는 동일한 도메인 지식을 IDE +안에서 사용하고 싶을 때가 있습니다 — `*.object.ts`를 직접 편집하거나, +플로우를 설계하거나, Cursor에게 CEL 술어를 작성하라고 요청할 때처럼요. + +ObjectOS는 코딩 어시스턴트에게 모든 종류의 ObjectOS 메타데이터를 +작성하는 방법을 가르치는 **9개의 퍼스트파티 에이전트 skills**를 +제공합니다. 이 skills는 오픈 [`skills`](https://www.npmjs.com/package/skills) +생태계(Vercel Labs)를 통해 배포되며 **Claude Code, Cursor, Copilot, +Codex, Gemini CLI, Windsurf, Cline, Continue, Roo, Goose, Kiro, +opencode** 및 그 외 약 50개의 에이전트와 함께 작동합니다. + +## 설치 + +`@objectstack/*` 패키지를 사용하는 모든 프로젝트에서: + +```bash +npx skills add objectstack-ai/framework +``` + +CLI는 어떤 에이전트가 구성되어 있는지(`.claude/`, +`.cursor/`, `.github/copilot/`, `AGENTS.md` 등) 감지하고 skills를 +적절한 위치에 작성합니다. 최신 버전을 가져오려면 언제든지 다시 +실행하세요. + +> `package.json` 변경도, 전역 설치도 없습니다. Skills는 리포지토리에 +> 커밋되는 일반 마크다운 파일이므로 팀 전체와 CI 에이전트가 동일한 +> 지침을 공유합니다. + +## 무엇을 얻나요 + +| Skill | 작업 대상이… 일 때 로드 | +|:------|:-----------------------------| +| `objectstack-platform` | `defineStack`, 드라이버, 어댑터, 플러그인, 서비스, `os` CLI, 배포 | +| `objectstack-data` | `*.object.ts`, `*.seed.ts`, 필드, 관계, 검증, 인덱스, 라이프사이클 훅, RLS | +| `objectstack-query` | ObjectQL — 필터, 정렬, 페이지네이션, 집계, 조인, 윈도우 함수, 전문 검색 | +| `objectstack-ui` | Views, Apps, Pages, Dashboards, Reports, Charts, Actions | +| `objectstack-automation` | Flows, Workflows, Triggers, Approvals, 스케줄, 웹훅 | +| `objectstack-ai` | Agents, Tools, Skills, Conversations, Model Registry, MCP | +| `objectstack-api` | REST/GraphQL 엔드포인트, 인증, 실시간, 에러 엔벨로프 | +| `objectstack-i18n` | 번역 번들, 로케일 폴백, 커버리지 | +| `objectstack-formula` | CEL 표현식 — 수식 필드, 검증/공유/가시성 술어, 조건 | + +각 skill은 **스코프를 인식**합니다 — 언제 자신을 로드하고 언제 형제 +skill에게 위임할지 에이전트에게 알려주므로, Claude가 platform skill로 +객체를 모델링하거나 data skill로 CEL 표현식을 작성하려고 시도하지 +않습니다. + +## 작동 방식 + +모든 skill은 +[framework repo](https://github.com/objectstack-ai/framework/tree/main/skills)의 +`skills/` 아래에 있는 폴더입니다: + +``` +skills/objectstack-data/ +├── SKILL.md # YAML frontmatter + prose guide +├── references/ +│ └── _index.md # pointers into @objectstack/spec Zod sources +└── rules/ # optional deep-dive rules +``` + +`SKILL.md` frontmatter는 `skills add`가 읽어서 skill을 적절한 +에이전트 표면으로 라우팅하는 내용입니다: + +```yaml +--- +name: objectstack-data +description: Design ObjectStack data schemas — objects, fields, ... +license: Apache-2.0 +compatibility: Requires @objectstack/spec Zod schemas (v4+) +metadata: + domain: data + tags: object, field, validation, relationship, hook, permission, rls +--- +``` + +모든 skill에 걸친 핵심 규칙: **`@objectstack/spec`의 Zod 스키마가 +진실입니다**. Skills는 의도를 제공하고, 에이전트는 정확한 필드 형태를 +위해 `node_modules/@objectstack/spec/src/**/*.zod.ts`의 스키마를 +읽습니다. 이것이 바로 skills를 설치하면 환각으로 만들어진 필드가 +사실상 사라지는 이유입니다. + +## Skills vs. AI Builder + +| | [AI Builder](./ai-builder) | IDE Skills | +|:--|:--|:--| +| 실행 위치 | Console (브라우저) | 당신의 IDE / 코딩 에이전트 | +| 변경 대상 | 테넌트의 라이브 메타데이터 | 리포지토리의 파일 | +| 대행 대상 | 최종 사용자 / 관리자 | 개발자 | +| 승인 모델 | HITL 큐 (`ai:approve`) | 풀 리퀘스트 리뷰 | +| 적합한 용도 | 일상적인 확장, 노코드 사용자 | 템플릿, 패키지, 복잡한 플로우, 라이브러리 코드 | + +이 둘은 상호 보완적입니다. 대부분의 팀은 테넌트 커스터마이징에는 AI +Builder를, 그리고 **게시할 의도가 있는 패키지** 작업에는 IDE skills를 +사용합니다(자세한 내용은 [Templates](./templates) 및 [Marketplace](./marketplace) 참조). + +## 업데이트 + +Skills는 `@objectstack/spec` 버전을 따릅니다. spec 패키지를 업그레이드한 +후 `npx skills add objectstack-ai/framework`를 다시 실행하여 에이전트를 +최신 Zod 스키마, CEL 헬퍼, 메타데이터 어휘와 동기화 상태로 유지하세요. + +## 관련 항목 + +- [AI Builder](./ai-builder) — Console 내의 대응 기능 +- [Agents](./agents) — ObjectOS *내부*에서 실행되는 서버 측 에이전트 선언 +- [Templates](./templates) — 모든 공식 템플릿은 이 skills를 로드한 상태로 구축됩니다 diff --git a/content/docs/build/data-model.ko.mdx b/content/docs/build/data-model.ko.mdx new file mode 100644 index 0000000..fbeda65 --- /dev/null +++ b/content/docs/build/data-model.ko.mdx @@ -0,0 +1,260 @@ +--- +title: 데이터 모델 +description: 객체, 필드, 관계, 유효성 검사, 인덱스 — AI에게 설명하거나 TypeScript로 작성합니다. +--- + +# 데이터 모델 + +데이터 모델은 앱의 단일 진실 공급원(single source of truth)입니다. 객체가 +한번 존재하게 되면, ObjectOS는 REST API, Console 뷰, RBAC +체크포인트, 감사 로그 항목, 그리고 AI 도구 노출을 — 무료로 제공합니다. + +**대부분의 고객은 스키마를 직접 손으로 작성하지 않습니다.** 그들은 필요한 +것을 [AI Builder](/docs/build/ai-builder)에서 설명하고, 플랫폼이 +객체, 필드, 인덱스, 번역을 생성합니다. 이 +페이지에서는 그 기저의 형태를 설명합니다 — 그래서 AI가 무엇을 +생성하는지 이해하고, 원할 때 직접 편집할 수 있습니다. + +## 작성 경로 + +| 경로 | 형태 | +|---|---| +| **AI Builder** (기본) | *"subject, description, priority, status, assignee를 가진 `support_ticket` 객체를 생성해줘."* | +| **Console 클릭 빌드** | Console → Objects → New Object → 폼 | +| **TypeScript (`*.object.ts`)** | 아래에 표시된 TS — 일반적으로 포크된 [템플릿](/docs/build/templates) 내부 | + +세 가지 모두 동일한 스키마를 생성합니다. 스키마가 표준(canonical)이며, 그 밖의 +모든 것은 파생됩니다. + +## 객체의 구조 + +```ts +// src/objects/task.ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Task = ObjectSchema.create({ + name: 'todo_task', + label: 'Task', + pluralLabel: 'Tasks', + icon: 'check-square', + description: 'A single unit of work.', + + fields: { + subject: Field.text({ label: 'Subject', required: true, maxLength: 200 }), + description: Field.markdown({ label: 'Description' }), + status: Field.select({ + label: 'Status', + options: [ + { label: 'To Do', value: 'todo', default: true }, + { label: 'In Progress', value: 'in_progress' }, + { label: 'Done', value: 'done' }, + ], + }), + due: Field.date({ label: 'Due' }), + assignee: Field.lookup('sys_user', { label: 'Assignee' }), + }, + + enable: { + trackHistory: true, // record field changes in audit log + apiEnabled: true, // expose REST endpoints (default true) + feeds: true, // chatter / comments / @mentions + }, +}); +``` + +스택에 등록합니다: + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects'; + +export default defineStack({ + manifest: { id: 'my.app', namespace: 'myapp', version: '0.1.0', type: 'app', name: 'My App' }, + objects: Object.values(objects), +}); +``` + +필요한 것은 그게 전부입니다. `os dev`가 재컴파일하면, `/api/v1/data/todo_task`, +Console Task 뷰, 그리고 Console 권한 행이 모두 나타납니다. + +## 필드 타입 + +ObjectStack은 약 25가지 필드 타입을 제공합니다. 가장 많이 사용되는 것들: + +### 스칼라 + +| 타입 | 저장하는 것 | 헬퍼 | +|---|---|---| +| `text` | 짧은 문자열 | `Field.text({ maxLength, required })` | +| `textarea` | 긴 문자열 | `Field.textarea(...)` | +| `markdown` | 마크다운이 포함된 리치 텍스트 | `Field.markdown(...)` | +| `number` | 정수 | `Field.number({ min, max })` | +| `decimal` | 정확한 소수 (금액 등) | `Field.decimal({ precision, scale })` | +| `boolean` | 참/거짓 | `Field.boolean({ defaultValue })` | +| `date` | 달력 날짜 | `Field.date(...)` | +| `datetime` | 타임스탬프 | `Field.datetime(...)` | +| `email` | 유효성 검증된 이메일 | `Field.email(...)` | +| `url` | 유효성 검증된 URL | `Field.url(...)` | +| `phone` | 유효성 검증된 전화번호 | `Field.phone(...)` | +| `json` | 임의의 JSON | `Field.json(...)` | + +### 선택 + +| 타입 | 용도 | +|---|---| +| `select` | 단일 선택 (열거형) | +| `multiselect` | 다중 선택 | + +### 관계 + +| 타입 | 카디널리티 | 헬퍼 | +|---|---|---| +| `lookup` | 일대다 (FK) | `Field.lookup({ reference: 'sys_user' })` | +| `masterDetail` | 연쇄 삭제가 있는 일대다 | `Field.masterDetail({ reference: 'order' })` | + +### 파일 및 미디어 + +| 타입 | 저장하는 것 | +|---|---| +| `attachment` | 스토리지 서비스를 통한 단일 파일 | +| `image` | 미리보기가 있는 이미지 첨부 | + +### 계산 / 파생 + +| 타입 | 동작 | +|---|---| +| `formula` | CEL 표현식으로부터 읽기 시점에 계산됨 | +| `rollup` | 연관 레코드의 집계 (합계/개수/평균) | +| `autoNumber` | 시퀀스 (`INV-{000001}`) | +| `created`, `lastModified` | 시스템이 관리하는 타임스탬프 | +| `createdBy`, `lastModifiedBy` | 시스템이 관리하는 사용자 참조 | + +## 필수 / 고유 / 기본값 + +모든 스칼라 필드에서 사용되는 공통 수정자: + +```ts +Field.text({ + label: 'Code', + required: true, + unique: true, // unique constraint enforced at DB level + defaultValue: '', + helpText: 'Internal short code', +}) +``` + +## 유효성 검사 + +인라인: + +```ts +Field.number({ label: 'Quantity', min: 1, max: 9999 }) +Field.text({ label: 'SKU', pattern: '^[A-Z]{3}-[0-9]{4}$' }) +``` + +객체 수준 규칙 (필드 간): + +```ts +ObjectSchema.create({ + name: 'order', + fields: { /* ... */ }, + validations: [ + { + name: 'discount_lt_total', + message: 'Discount cannot exceed total', + condition: 'discount < total', + }, + ], +}); +``` + +유효성 검사는 모든 쓰기 작업 — REST, Console, ObjectQL — 에서 실행되므로, +"뒷문(back door)"은 존재하지 않습니다. + +## 인덱스 및 성능 + +```ts +ObjectSchema.create({ + name: 'order', + fields: { /* ... */ }, + indexes: [ + { fields: ['status', 'created_at'] }, + { fields: ['account', 'created_at'], unique: false }, + ], +}); +``` + +드라이버는 스키마 동기화 시 실제 DB 인덱스를 생성합니다. + +## 필드 그룹 + +긴 폼의 경우, Console에서 필드를 그룹화합니다: + +```ts +ObjectSchema.create({ + name: 'task', + fieldGroups: [ + { key: 'core', label: 'Task', icon: 'check-square' }, + { key: 'planning', label: 'Planning', icon: 'calendar' }, + { key: 'meta', label: 'Metadata', icon: 'info', defaultExpanded: false }, + ], + fields: { + subject: Field.text({ label: 'Subject', group: 'core' }), + due: Field.date({ label: 'Due', group: 'planning' }), + }, +}); +``` + +## 라이프사이클 및 소유권 + +```ts +ObjectSchema.create({ + name: 'task', + ownership: 'own', // 'own' | 'shared' | 'system' + enable: { + apiEnabled: true, // generated REST endpoints + trackHistory: true, // audit log of field changes + feeds: true, // sys_comment / sys_activity / @mentions + softDelete: true, // tombstone instead of hard delete + }, +}); +``` + +## 시스템 객체 (모든 프로젝트에 무료 제공) + +이것들은 선언할 필요가 없습니다 — 항상 존재합니다: + +| 객체 | 내용 | +|---|---| +| `sys_user` | 사용자 계정 | +| `sys_org` | 조직 / 테넌트 | +| `sys_member` | 조직 멤버십 | +| `sys_role`, `sys_permission_set` | RBAC 기본 요소 | +| `sys_audit_log` | 감사 추적 (감사 기능이 로드되었을 때) | +| `sys_file`, `sys_attachment` | 파일 메타데이터 (스토리지가 로드되었을 때) | +| `sys_comment`, `sys_activity` | 피드 / chatter (피드가 로드되었을 때) | +| `sys_session`, `sys_api_key` | 인증 아티팩트 | +| `sys_webhook`, `sys_webhook_delivery` | 웹훅 구독 (활성화되었을 때) | + +`lookup` 필드에서 이름으로 참조합니다 — 예: `Field.lookup({ reference: 'sys_user' })`. + +## 다형성 플랫폼 기능 + +`feeds: true`와 `trackHistory: true`를 활성화하면, 객체가 +자동으로 다음에 참여합니다: + +- `sys_comment` (thread_id = `:`) +- `sys_attachment` (parent_object = ``, parent_id = ``) +- `sys_activity` (타임라인) +- `sys_audit_log` (필드 수준 차이) + +이것들을 객체별로 연결할 필요가 없습니다 — 플랫폼에서 다형성으로 처리됩니다. + +## 다음 단계 + +- [권한](/docs/configure/permissions) — 객체에 대한 접근을 통제합니다 +- [플로우 / 자동화](/docs/build/flows) — 레코드 변경에 반응합니다 +- [API 접근](/docs/configure/api-access) — 생성된 REST를 호출합니다 +- [`os explain`](/docs/reference/cli) — 렌더링된 스키마를 출력합니다 +- [`@objectstack/spec` 소스](https://github.com/objectstack-ai/framework/tree/main/packages/spec) — 스키마가 계약이며, 여기의 모든 것은 그로부터 파생됩니다 diff --git a/content/docs/build/flows.ko.mdx b/content/docs/build/flows.ko.mdx new file mode 100644 index 0000000..3ddedb1 --- /dev/null +++ b/content/docs/build/flows.ko.mdx @@ -0,0 +1,233 @@ +--- +title: 플로우 및 자동화 +description: 선언형 비즈니스 로직 — AI에게 설명하거나 TypeScript로 작성하면, 런타임은 어느 쪽이든 동일한 아티팩트를 실행합니다. +--- + +# 플로우 및 자동화 + +플로우는 서버를 작성하지 않고도 비즈니스 로직을 표현하는 방법입니다. +모든 플로우는 런타임이 실행하는 선언형 메타데이터로, 객체나 뷰와 +동일합니다. 즉, 플로우는 `os diff`, 감사 로그, Console의 플로우 빌더, +그리고 [AI Builder](/docs/build/ai-builder)에 한꺼번에 나타납니다. + +대부분의 고객은 AI에게 요청하여 플로우를 만듭니다. + +> *"우선순위가 높은 티켓이 'new' 상태로 30분간 머물러 있으면, +> Slack으로 매니저에게 알려줘."* + +AI가 아래 플로우를 생성합니다. 이 페이지에서는 읽고 편집할 수 있도록 +그 구조를 설명합니다. + +스택에서 해당 기능을 활성화하세요. + +```ts +export default defineStack({ + // ... + requires: ['automation'], +}); +``` + +## 세 가지 플로우 유형 + +| 유형 | 트리거 | 사용 사례 | +|---|---|---| +| **Autolaunched** | 레코드 변경 (insert/update/delete) | "사용자 등록 시 환영 이메일 보내기" | +| **Scheduled** | Cron 표현식 또는 간격 | "매일 밤 오래된 작업 표시하기" | +| **Manual** | 사용자가 Console에서 버튼을 클릭하거나 API 호출 | "송장 승인" 액션 | + +## Autolaunched: 레코드 변경에 반응하기 + +```ts +// src/flows/welcome_email.ts +import { defineFlow } from '@objectstack/spec'; + +export const welcomeEmail = defineFlow({ + name: 'welcome_email', + type: 'autolaunched', + trigger: { + object: 'sys_user', + when: 'after_insert', + }, + steps: [ + { + type: 'action', + action: 'send_email', + inputs: { + to: '{!trigger.record.email}', + subject: 'Welcome to {!org.name}', + body: 'Hi {!trigger.record.name}, welcome aboard.', + }, + }, + ], +}); +``` + +변수 보간: `{!trigger.record.}`, `{!org.}`, +`{!user.}`, `{!step..output}`. `condition:` 블록에서는 +CEL 표현식을 사용하세요. + +트리거 타이밍: + +| `when` | 실행 시점 | +|---|---| +| `before_insert` | 쓰기 트랜잭션 내부, INSERT 이전 | +| `after_insert` | 커밋 이후 | +| `before_update` | 쓰기 트랜잭션 내부, UPDATE 이전 | +| `after_update` | 커밋 이후 | +| `before_delete` | 쓰기 트랜잭션 내부, DELETE 이전 | +| `after_delete` | 커밋 이후 | + +`before_*` 플로우는 작성 중인 레코드를 변경할 수 있습니다(필드 계산, +데이터 정규화). `after_*` 플로우는 비동기로 실행되며 느린 외부 서비스를 +호출할 수 있습니다. + +## Scheduled: 정해진 시각에 실행하기 + +```ts +export const nightlyCleanup = defineFlow({ + name: 'nightly_cleanup', + type: 'scheduled', + schedule: { cron: '0 2 * * *', timezone: 'America/New_York' }, + steps: [ + { + type: 'query', + query: { object: 'task', filter: 'status:open AND due_lt:now()' }, + output: 'stale', + }, + { + type: 'foreach', + items: '{!step.stale}', + do: [ + { type: 'update', record: '{!item.id}', fields: { status: 'overdue' } }, + ], + }, + ], +}); +``` + +`@objectstack/service-job` 기능이 이를 지원합니다 — +[Runtime Capabilities](/docs/reference/runtime-capabilities)를 참조하세요. + +## Manual: 액션 및 승인 + +```ts +export const approveInvoice = defineFlow({ + name: 'approve_invoice', + type: 'manual', + inputs: { + invoice_id: { type: 'lookup', reference: 'invoice', required: true }, + note: { type: 'textarea' }, + }, + steps: [ + { + type: 'update', + record: '{!inputs.invoice_id}', + fields: { status: 'approved', approved_by: '{!user.id}' }, + }, + ], +}); +``` + +Console에서 Invoice 뷰의 버튼으로 노출하거나, REST를 통해 호출하세요. + +```bash +curl -X POST https://app.example.com/api/v1/actions/invoice/approve_invoice \ + -H 'Authorization: Bearer ' \ + -d '{"inputs": {"invoice_id": "inv_123", "note": "OK"}}' +``` + +## 스텝 유형 + +| 스텝 | 용도 | +|---|---| +| `query` | ObjectQL을 통해 레코드 읽기 | +| `create` / `update` / `delete` | 객체에 쓰기 | +| `action` | 내장 또는 플러그인 등록 액션 호출 (이메일, 웹훅, AI 호출 등) | +| `condition` | CEL 표현식으로 분기 | +| `foreach` | 컬렉션 순회 | +| `parallel` | 하위 스텝을 동시에 실행 | +| `wait` | 일정 시간 / 특정 시각까지 / 조건 충족까지 일시 정지 | +| `subflow` | 다른 플로우 호출 | +| `approval` | 사용자가 승인할 때까지 차단 (`@objectstack/plugin-approvals` 필요) | + +## 조건 및 분기 + +```ts +{ + type: 'condition', + when: 'trigger.record.amount > 10000', + then: [ + { type: 'action', action: 'send_slack', inputs: { /* ... */ } }, + ], + else: [ + { type: 'update', record: '{!trigger.record.id}', fields: { status: 'auto_approved' } }, + ], +} +``` + +## 오류 처리 + +각 스텝은 다음을 받습니다: + +```ts +{ + type: 'action', + action: 'send_email', + inputs: { /* ... */ }, + retry: { attempts: 3, backoffMs: 1000, multiplier: 2 }, + onError: 'continue' | 'fail' | 'rollback', +} +``` + +Autolaunched `before_*` 플로우의 경우, `onError: 'fail'`(기본값)은 +원본 쓰기 트랜잭션을 중단시킵니다. `after_*` 플로우의 경우, 원본 쓰기는 +이미 커밋된 상태이며, 실패한 플로우 실행은 작업 재시도 큐로 들어갑니다. + +## 수식 및 표현식 (CEL) + +조건, 동적 필드 값, 필터 표현식은 모두 **CEL**(Common Expression +Language) — 안전한 표현식 평가를 위한 Google의 언어 — 을 받습니다: + +```ts +'amount > 10000 && account.tier == "enterprise"' +'duration(now() - created_at) > duration("30d")' +'has(record.notes) && record.notes != ""' +``` + +CEL은 샌드박스로 격리되어 있고(부수 효과 없음, I/O 없음), 서버 측에서 +평가되며, 플로우 빌더에서 감사가 가능합니다. + +## 비주얼 빌더 + +Console에는 선언형 메타데이터와 양방향으로 변환되는 비주얼 플로우 +빌더가 함께 제공됩니다 — 비엔지니어도 플로우를 편집할 수 있으며, 직접 +손으로 작성하는 TypeScript와 동일한 구조로 다시 직렬화됩니다. + +## 플로우 테스트 + +```bash +os test --scenario "welcome email fires on signup" +``` + +## 한계 및 모범 사례 + +- **before 훅은 작게 유지하세요.** 쓰기 트랜잭션을 차단합니다. +- **장시간 실행되는 스텝 대신 `wait`를 사용하세요.** 슬립하는 플로우는 + 워커를 차단하지만, `wait until`은 워커를 풀로 반환합니다. +- **독립적인 스텝에는 `parallel`을 사용하세요.** 기본값은 순차 실행입니다. +- **멱등성이 중요합니다.** 재시도는 동일한 스텝을 두 번 실행할 수 + 있으므로, 외부 부수 효과는 중복을 제거해야 합니다(플로우 실행 id를 + 키로 사용하세요). +- **감사에 민감한 액션.** 권한을 변경하거나 레코드를 삭제하는 플로우는 + 스스로 `sys_audit_log`에 로그를 남겨야 합니다. + +## 다음으로 볼 내용 + +- [Webhooks](/docs/configure/webhooks) — 아웃바운드 알림으로, 흔히 + 플로우에서 트리거됩니다 +- [Email](/docs/configure/email) — `send_email` 액션의 전송 수단 +- [AI Service](/docs/configure/ai) — LLM 스텝을 위한 `ai_call` 액션 +- [API Access](/docs/configure/api-access) — 외부 시스템에서 manual + 플로우 호출하기 +- [@objectstack/service-automation](https://github.com/objectstack-ai/framework/tree/main/packages/services/service-automation) + — 실행 엔진의 소스 diff --git a/content/docs/build/index.ko.mdx b/content/docs/build/index.ko.mdx new file mode 100644 index 0000000..20bc62f --- /dev/null +++ b/content/docs/build/index.ko.mdx @@ -0,0 +1,61 @@ +--- +title: 빌드 +description: ObjectOS에서 앱이 만들어지는 방식 — AI와 대화하거나, Console에서 클릭하거나, 템플릿을 포크하여. +--- + +# 빌드 + +ObjectOS에서 고객은 메타데이터를 작성하지 않습니다. **원하는 것을 +설명하면 AI가 만들어 줍니다.** 대부분의 사람들이 실제로 플랫폼을 +사용하는 순서에 따라 세 가지 경로가 있습니다: + +| 경로 | 대상 | 결과물 | +|---|---|---| +| **[AI Builder](/docs/build/ai-builder)** (주요) | 비즈니스 사용자, 제품 책임자, 누구나 | 테넌트의 라이브 메타데이터, 승인 대기열에 등록 | +| **Console 클릭 빌드** (대안) | 폼을 선호하는 관리자 | 동일한 라이브 메타데이터 | +| **[템플릿 포크](/docs/build/templates)** (개발 경로) | 소스 관리 하에 코드를 두고자 하는 엔지니어 | marketplace를 통해 배포되는 TypeScript 패키지 | + +세 가지 경로 모두 **동일한 형태의 산출물** — 객체, 필드, 뷰, 액션, +플로우, 권한의 패키지를 생성합니다. 플랫폼은 메타데이터가 어떻게 +만들어졌는지 신경 쓰지 않습니다. + +## 플랫폼 용어 + +모든 앱은 동일한 기본 요소로 구성됩니다. 이것들을 한 번만 익혀 두세요. + +| 개념 | 무엇인가 | 문서 | +|---|---|---| +| **[Package](/docs/build/packages)** | 조직화의 단위 — `com.acme.crm`, 버전 관리, 설치 가능 | Packages | +| **[Data model](/docs/build/data-model)** | 객체 + 필드 + 관계 + 상태 머신 | Data Model | +| **[Actions](/docs/build/actions)** | REST, Console 버튼, 플로우, AI 에이전트에서 호출 가능한 명명된 작업 | Actions | +| **[Flows](/docs/build/flows)** | 선언적 비즈니스 로직 (자동 실행 / 예약 / 수동) | Flows | +| **[Agents](/docs/build/agents)** | 최종 사용자 AI 어시스턴트 — Agent → Skill → Tool | Agents | +| **[Marketplace](/docs/build/marketplace)** | 누구나 원클릭으로 설치할 수 있는 기성 패키지 | Marketplace | + +## 어디서 시작할까 + +1. **그냥 둘러보는 중인가요?** → Console을 열고 [AI Builder](/docs/build/ai-builder)를 시작하여, + *"우선순위, 상태, 담당자가 있는 고객 지원 티켓을 추적하고 싶어요."* 라고 말해 보세요. + 30초 안에 작동하는 객체를 갖게 됩니다. + +2. **그 위에 실제 제품을 구축하나요?** → AI가 생성하는 것을 이해할 수 + 있도록 [Packages](/docs/build/packages)와 [Data Model](/docs/build/data-model)을 + 읽어 보세요. AI로 초안을 작성하고, 직접 또는 대화로 다듬은 뒤, + 결과를 커밋하세요. + +3. **Salesforce/Retool 앱을 대체하나요?** → 가장 가까운 + [marketplace 템플릿](/docs/build/marketplace)을 포크하고, 필요 없는 것을 + 제거한 뒤, AI에게 빈 부분을 채워 달라고 요청하세요. + +## 절대 하지 않아도 되는 일 + +- REST 엔드포인트 작성. 플랫폼이 객체마다 하나씩 생성합니다. +- Console 폼 연결. 폼은 스키마로부터 렌더링됩니다. +- RBAC 검사 작성. 권한은 코딩되는 것이 아니라 선언됩니다. +- 관리자 UI 구축. Console + Account가 시스템 및 비즈니스 관리 모두를 다룹니다. +- 타입 생성. `@objectstack/spec`이 스키마로부터 타입을 도출합니다. + +ObjectOS는 의도적으로 이 점에 대해 확고합니다: **메타데이터가 일단 +존재하면, 모든 표면(REST, Console, ObjectQL, 감사, AI 도구)이 +무료로 나타납니다.** 그렇기 때문에 AI Builder가 작동하는 것입니다 — +메타데이터만 생성하면 되고, 나머지는 플랫폼이 처리합니다. diff --git a/content/docs/build/marketplace.ko.mdx b/content/docs/build/marketplace.ko.mdx new file mode 100644 index 0000000..49f2854 --- /dev/null +++ b/content/docs/build/marketplace.ko.mdx @@ -0,0 +1,121 @@ +--- +title: marketplace +description: 코드를 작성하지 않고 실행 중인 ObjectOS에 완성된 앱을 설치합니다. +--- + +# marketplace + +ObjectOS marketplace를 사용하면 실행 중인 런타임에 사전 빌드된 앱을 설치할 수 +있습니다. 빌드 단계도, 재시작도, 소스 체크아웃도 필요하지 않습니다. 첫날부터 +실제 소프트웨어를 사용자에게 선보이는 가장 빠른 방법입니다. + +## 작동 방식 + +모든 ObjectOS 런타임에는 `MarketplaceProxy`와 `MarketplaceInstallLocal` +플러그인이 기본적으로 활성화되어 제공됩니다. Console(`/_console/`)을 열면 +marketplace 탭이 구성된 앱 카탈로그를 조회하고 설치 가능한 앱을 표시합니다. + +```text +You ─→ Console ─→ Marketplace tab ─→ pick app ─→ Install + ↓ + Artifact merged into kernel + ↓ + Console re-renders with new + objects / views / permissions + ↓ + Done — no restart +``` + +## 카탈로그 + +| 카탈로그 | 소스 | 사용 시점 | +|---|---|---| +| **Default** | 런타임 이미지에 사전 번들됨 | 첫 평가, 데모, 오프라인 | +| **ObjectStack public catalog** | 공개 앱 레지스트리 | 최신 커뮤니티 + 퍼스트파티 앱 | +| **Private catalog** | 직접 게시한 아티팩트 | 공개하고 싶지 않은 내부 앱 | +| **Local** | 런타임에 마운트된 파일 | 에어갭 환경, 커스텀 빌드 | + +카탈로그 소스는 marketplace 플러그인 또는 환경 변수를 통해 구성됩니다. +[Runtime Configuration](/docs/configure/runtime)을 참조하세요. + +## 기본 카탈로그에 포함된 내용 + +이 앱들은 Console에서 클릭 한 번으로 설치할 수 있습니다. + +| 앱 | 제공 기능 | +|---|---| +| **Todo** | 범용 작업 및 프로젝트 추적기 | +| **Contracts** | AI 조항 추출 기능이 포함된 계약 수명 주기(CLM) | +| **Procurement** | 공급업체, 발주서, 3자 대사(3-way match) | +| **Compliance** | SOC 2 / ISO 27001 통제 항목 + 증적 수집 | +| **Helpdesk** | AI 우선 고객 지원 티켓팅 | +| **Content** | 편집 캘린더 + 채널 ROI | +| **HR** | 디렉터리, 조직도, 휴가 관리 | +| **Project** | 프로젝트 / 작업 / 마일스톤 추적 | + +소스는 +[github.com/objectstack-ai/templates](https://github.com/objectstack-ai/templates)에 +있습니다. 이들 중 아무거나 복제하여 커스텀 앱의 출발점으로 삼으세요. + +## 설치 흐름 + +1. **Console 열기** — http://localhost:3000/_console/ +2. **로그인** — 아직 계정이 없다면 `/_account/register`에서 등록합니다 +3. **marketplace 탭으로 이동** +4. **앱을 선택하고 Install 클릭** +5. **Console 새로고침** — 새 앱의 오브젝트, 뷰, 플로우가 나타납니다 + +내부적으로 marketplace는 앱의 컴파일된 아티팩트를 가져와 실행 중인 커널에 +병합하고, 해당 오브젝트를 ObjectQL에 등록합니다. 시드 데이터는(앱이 정의한 +경우) 최초 설치 시 삽입됩니다. + +## 제거 + +Console → Marketplace → 설치된 앱 → Uninstall에서 제거합니다. 앱의 +오브젝트는 커널에서 제거되고 해당 테이블은 정리 대상으로 표시됩니다(데이터는 +기본적으로 유지되며, 테이블 삭제 여부는 사용자가 선택합니다). + +## 직접 만든 앱 게시하기 + +`os init`으로 빌드한 모든 것은 marketplace 앱이 될 수 있습니다. + +```bash +os init my-app -t app --install +cd my-app +# ... write objects, views, etc. ... +os compile # → dist/objectstack.json +os package publish # publish to a catalog +``` + +공개 카탈로그에 게시하려면 레지스트리 계정(`os login`)이 필요합니다. 프라이빗 +카탈로그에 게시하려면 `OS_PACKAGE_REGISTRY`를 직접 운영하는 레지스트리로 +지정하세요. + +## 버전 관리 + +게시된 모든 앱은 불변(immutable)입니다. 업데이트는 새 버전을 생성합니다. +런타임은 앱별로 설치된 버전과 사용 가능한 업데이트를 추적합니다. 추적 중인 +카탈로그에 새 버전이 게시되면 사용자는 Console에서 "Update available" 배지를 +보게 됩니다. + +## 권한 + +앱 설치에는 `manage_marketplace` 시스템 권한이 필요합니다. 기본적으로 +**Setup Administrator** 권한 집합의 구성원만 이 권한을 가집니다. 일반 +사용자에게는 marketplace가 읽기 전용으로 표시됩니다. + +## 에어갭 marketplace + +인터넷 송신이 없는 배포 환경에서는 네트워크 내부에 로컬 카탈로그 서버를 +실행하고 ObjectOS가 이를 가리키도록 합니다. 토폴로지는 +[Air-gapped](/docs/deploy/air-gapped)를 참조하세요. + +## marketplace가 아닌 것 + +- **코드 배포 채널이 아닙니다.** 게시된 앱은 컴파일된 아티팩트로, 데이터 + UI + + 플로우를 선언적으로 기술합니다. 임의의 JavaScript를 제공하지 않습니다. +- **샌드박스가 아닙니다.** 설치된 앱은 커널 내의 다른 무엇과 마찬가지로 + 데이터베이스에 동일하게 접근합니다. 신뢰할 수 없는 출처의 앱을 설치하기 + 전에는 아티팩트를 검토하세요. +- **(아직은) 결제 플랫폼이 아닙니다.** 공개 카탈로그의 앱은 무료 Apache-2.0 + 라이선스입니다. 상업적 / 유료 배포는 로드맵에 있습니다. diff --git a/content/docs/build/meta.ko.json b/content/docs/build/meta.ko.json new file mode 100644 index 0000000..8303ac0 --- /dev/null +++ b/content/docs/build/meta.ko.json @@ -0,0 +1,17 @@ +{ + "title": "구축", + "defaultOpen": false, + "pages": [ + "index", + "ai-builder", + "ai-skills", + "packages", + "data-model", + "views", + "actions", + "flows", + "agents", + "templates", + "marketplace" + ] +} diff --git a/content/docs/build/packages.ko.mdx b/content/docs/build/packages.ko.mdx new file mode 100644 index 0000000..5df7933 --- /dev/null +++ b/content/docs/build/packages.ko.mdx @@ -0,0 +1,136 @@ +--- +title: 패키지 +description: ObjectOS의 조직 단위 — 버전 관리되고, 설치 가능하며, 공유할 수 있습니다. +--- + +# 패키지 + +ObjectOS에서 만드는 모든 것은 **패키지** 안에 존재합니다. 패키지는 버전이 관리되고 자체적으로 완결된 메타데이터 번들입니다. 패키지는 AI Builder가 작업하는 단위이자, marketplace가 배포하는 단위이며, ObjectOS가 업데이트를 추적하는 단위입니다. + +## 패키지의 구성 요소 + +```text +com.acme.crm@1.2.0 +├── manifest id, version, namespace, dependencies +├── objects/ *.object.ts, *.state.ts, *.hook.ts +├── views/ *.view.ts, *.page.ts, *.form.ts +├── actions/ *.action.ts +├── flows/ *.flow.ts, *.approval.ts +├── agents/ *.agent.ts, *.skill.ts +├── permissions/ *.permission.ts +├── sharing/ *.sharing.ts +├── translations/ en.ts, zh-CN.ts, ... +├── apps/ *.app.ts (navigation) +└── data/ defineDataset(...) seed +``` + +모든 메타데이터 아티팩트(object, action, flow 등)는 정확히 하나의 패키지에 속합니다. 패키지 id는 **reverse-DNS**(`com.acme.crm`, `org.mycompany.helpdesk`) 형식이며, 네임스페이스 접두사(`crm_`, `hd_`)는 같은 테넌트에 설치된 여러 패키지 간에 테이블/object 이름이 충돌하지 않도록 합니다. + +## 패키지 생성하기 + +### AI Builder에서 + +> *"우리 내부 CRM용 새 패키지를 시작하고, 네임스페이스는 `crm`으로 해줘."* + +AI는 `create_package`와 `set_active_package`를 호출합니다. 그 이후로 당신이 설명하는 모든 object / field / action은 `crm`에 들어갑니다. + +### CLI에서 + +```bash +os init my-crm -t app +cd my-crm +# manifest lives in the `manifest:` block of ./objectstack.config.ts +pnpm dev +``` + +### Console에서 + +**Console → Packages → New Package** — 이름, id, 버전, 네임스페이스. + +## 활성 패키지 + +AI Builder에서는 대화가 **활성 패키지(active package)**를 가지고 있습니다. 이는 새 메타데이터의 기본 컨테이너입니다. 당신이 *"`com.acme.helpdesk`로 전환해줘."*와 같이 말하면 AI가 `set_active_package` 도구로 이를 설정합니다. CLI에서 활성 패키지는 단순히 프로젝트의 `objectstack.config.ts`(`manifest:` 블록)에 선언된 패키지입니다. + +## 버전 관리 + +패키지는 **semver**를 따릅니다. 런타임과 동일한 규칙입니다 — [Changelog & Versioning](/docs/resources/changelog)을 참고하세요. + +버전을 올리더라도 게시(publish)하기 전까지는 아무것도 바뀌지 않습니다. 런타임은 테넌트별, 패키지별로 `installed_version`을 추적하며, 카탈로그에 더 최신 버전이 있으면 marketplace에 *"Update available"* 배지가 표시됩니다. + +## 의존성 + +패키지는 다른 패키지에 의존할 수 있습니다: + +```json +{ + "id": "com.acme.helpdesk", + "version": "0.3.0", + "dependencies": { + "com.acme.crm": "^1.0.0", + "sys.feeds": "*" + } +} +``` + +런타임은 설치 시 의존성을 해석합니다. `com.acme.crm`이 설치되어 있지 않으면 helpdesk 설치는 명확한 오류와 함께 실패합니다. + +시스템 패키지(`sys.*`)는 항상 존재합니다 — feeds, attachments, audit, identity 등. + +## 게시하기 + +```bash +os compile # → dist/objectstack.json +os package publish # → ObjectStack cloud catalog +``` + +`os package publish`는 컴파일된 manifest를 `OS_CLOUD_URL`로 구성된 클라우드 컨트롤 플레인에 업로드하며, `OS_CLOUD_API_KEY`로 인증합니다. 비공개/에어갭(air-gapped) 배포의 경우 게시를 건너뛰고 컴파일된 `dist/objectstack.json`을 대상 설치 환경에 직접 전달하세요(아래 설치 섹션 참조). [Marketplace](/docs/build/marketplace)를 참고하세요. + +## 설치하기 + +| 경로 | 방법 | +|---|---| +| Console | Marketplace 탭 → 패키지 선택 → Install | +| REST | `POST /api/v1/marketplace/install-local` (본문: `{ packageId, versionId? }`) | +| 에어갭 | 컴파일된 `dist/objectstack.json` 아티팩트를 마운트 (참고: [Air-gapped](/docs/deploy/air-gapped)) | + +설치는 패키지의 메타데이터를 실행 중인 커널에 병합하고, 해당 object들을 ObjectQL에 등록하며, 최초 설치 시 초기 데이터를 시드합니다 — 재시작이 필요 없습니다. 캐시된 설치는 다음 부팅 시 다시 등록되므로 프로세스 재시작에도 유지됩니다. + +## 제거하기 + +제거는 패키지의 캐시된 manifest를 삭제하여 다음 부팅 시 더 이상 재등록되지 않도록 합니다. object 등록은 누적 방식이므로, 이미 실행 중인 패키지의 object를 완전히 언로드하려면 **커널 재시작**이 필요합니다. 데이터는 기본적으로 **유지**되므로, 다시 설치하여 이어서 사용할 수 있습니다. + +## 패키지 간 규약 + +여러 패키지가 공존할 때 충돌을 방지하기 위해: + +| 아티팩트 | 규약 | +|---|---| +| Object 이름 | 항상 접두사 사용: `crm_account`, `hd_ticket` | +| Action 이름 | 접두사 사용: `crm_assign_owner`, `hd_close_ticket` | +| Flow 이름 | 접두사 사용: `hd_overdue_alert` | +| 번역 키 | 네임스페이스 하위로 스코프 지정: `crm.account.label` | +| 시드 `externalId` | `:`, 예: `crm:demo-account-1` | +| 시스템 이름 | 예약됨: `sys_*` (커스텀 패키지에서 절대 사용 금지) | + +CLI와 AI Builder 모두 생성 시 이 규약을 강제합니다. + +## 시스템 패키지 + +런타임은 항상 존재하면서 다형적(polymorphic) 서비스를 제공하는 소수의 패키지를 함께 제공합니다: + +| 패키지 | 제공 항목 | +|---|---| +| `sys.identity` | `sys_user`, `sys_organization`, `sys_member`, 세션, API 키 | +| `sys.feeds` | `sys_comment`, `sys_activity`, `sys_attachment` | +| `sys.audit` | `sys_audit_log` | +| `sys.files` | `sys_file` | +| `sys.ai` | `ai_conversations`, `ai_pending_actions` | +| `sys.settings` | `sys_setting` | + +object에서 `enable: { feeds: true, trackHistory: true, … }`를 통해 활성화할 수 있습니다 — [Data Model](/docs/build/data-model)을 참고하세요. + +## 다음으로 볼 곳 + +- [Marketplace](/docs/build/marketplace) — 다른 테넌트에 패키지 배포 +- [Data Model](/docs/build/data-model) — 패키지 안에 들어가는 것 +- [`os package` commands](/docs/reference/cli) — 전체 CLI 레퍼런스 diff --git a/content/docs/build/templates.ko.mdx b/content/docs/build/templates.ko.mdx new file mode 100644 index 0000000..5e2aeff --- /dev/null +++ b/content/docs/build/templates.ko.mdx @@ -0,0 +1,106 @@ +--- +title: 템플릿 +description: 포크 가능한 스타터 패키지 — `todo`, `contracts`, `procurement`, `helpdesk` 등. +--- + +# 템플릿 + +템플릿은 **포크 가능한 스타터 패키지**입니다. 각 템플릿은 marketplace에서 +클릭 한 번으로 설치하거나, CLI로 클론하여 확장할 수 있는 TypeScript +코드베이스를 얻을 수 있는 실제로 형태를 갖춘 앱입니다. + +템플릿이 존재하는 두 가지 이유: + +1. **말보다 보여주기** — 동작하는 멀티 오브젝트 앱은 문서보다 더 빠르게 + 읽힙니다. +2. **엔지니어링 팀에 출발점 제공** — DB에 AI가 생성한 메타데이터만이 아니라, + 소스 관리 하에 있는 코드를 원할 때. + +## 기본 카탈로그에 포함되는 것 + +| 템플릿 | 도메인 | 포크 가능한 스타터 용도… | +|---|---|---| +| **`todo`** | 작업 및 프로젝트 | "어떤 식으로든 그룹화된 할 일" 도구 일체 | +| **`contracts`** | AI 조항 추출 기능이 있는 CLM | 승인 + 문서 AI | +| **`procurement`** | 공급업체, 발주서, 3-way 매칭 | 승인 체인 + 입고 | +| **`compliance`** | SOC 2 / ISO 27001 통제 | 증거 수집 워크플로우 | +| **`helpdesk`** | AI 우선 고객 지원 | 티켓 + SLA + AI 코파일럿 | +| **`content`** | 편집 일정 + 채널 ROI | 라이프사이클 + 지표 | +| **`hr`** | 디렉터리, 조직도, 휴가 | 인력 중심 앱 | +| **`project`** | 프로젝트 / 작업 / 마일스톤 추적 | PM 도구 | + +출처: [github.com/objectstack-ai/templates](https://github.com/objectstack-ai/templates). +Apache-2.0. 묻지 말고 포크하세요. + +## 30초 만에 설치 (코드 불필요) + +Console에서: + +1. **Marketplace**를 열고 → 템플릿을 검색합니다. +2. **Install**을 클릭합니다. +3. 새로고침하면 — 새 앱이 내비게이션에 나타납니다. + +완료. 레코드, 뷰, 플로우, 권한 — 모두 즉시 사용 가능합니다. + +## 코드베이스로 포크 (엔지니어용) + +```bash +pnpm dlx @objectstack/cli create my-app --template todo +cd my-app +pnpm install +pnpm dev # http://localhost:4002 +``` + +클론은 자체 완결적인 pnpm 패키지로 — `*.object.ts`, `*.view.ts`, +`*.flow.ts` 파일을 편집, 린트, 테스트, 커밋할 수 있습니다. 만족스러우면: + +```bash +pnpm build +os package publish # → your marketplace +``` + +여러분의 포크를 설치하는 고객은 **여러분의** 브랜드와 **여러분의** 수정 +사항을 받게 됩니다. + +## 템플릿 헌장 (사회적 계약) + +모든 템플릿은 범위와 엄격한 한계를 선언하는 `CHARTER.md`를 함께 제공합니다 — +일반적으로: 비즈니스 오브젝트 6개 이하, `src/` 하위 2,500 LOC 이하, 앱 1개, +기본 로케일 1개. 헌장은 템플릿이 절반만 완성된 제품이 아니라 **템플릿**으로 +유지되도록 하기 위해 존재합니다. 포크가 이러한 한계를 초과하면, 기존 템플릿을 +부풀리지 말고 새로운 템플릿으로 출시하세요. + +파일 접미사 프로토콜(`*.object.ts`, `*.state.ts`, `*.hook.ts`, …), +작성 순서(오브젝트 → 상태 → 훅 → 뷰 → 플로우 → …), 그리고 재발명하는 대신 +재사용해야 하는 다형성 플랫폼 서비스(`sys_comment`, `sys_attachment`, +`sys_audit_log`)에 대해서는 업스트림 +[TEMPLATE_GUIDE.md](https://github.com/objectstack-ai/templates/blob/main/TEMPLATE_GUIDE.md)를 +참고하세요. + +## 출발점 선택하기 + +| 만들고 있는 것이… | 이것을 포크하세요 | 이유 | +|---|---|---| +| "무언가"를 추적하는 내부 도구 일체 | `todo` | 모든 프리미티브를 가장 작고 깔끔하게 보여줌 | +| 승인 체인이 있는 무엇이든 | `procurement` 또는 `contracts` | 실제 승인 프로세스가 연결되어 있음 | +| AI가 포함된 고객 대면 무엇이든 | `helpdesk` | 에이전트 + 티켓 코파일럿 패턴 | +| 규제 산업 워크플로우 | `compliance` | 감사 + 증거 + 통제 매핑 | +| 인력 / 조직도 앱 | `hr` | 디렉터리 + 보고 구조 | + +## AI Builder + 템플릿 함께 사용하기 + +템플릿을 포크하고 **그 위에서** AI Builder를 **여전히** 사용할 수 있습니다. +`helpdesk`를 설치한 다음 이렇게 말하세요: + +> *"`hd_ticket`에 `csat_score` 정수 필드를 추가하고, 범위는 1–5, 마감 시 +> 입력하도록 요청해줘."* + +AI는 설치된 패키지를 그 자리에서 확장합니다. `os package export`로 변경 +사항을 소스 저장소에 푸시하세요. + +## 다음으로 갈 곳 + +- [AI Builder](/docs/build/ai-builder) — 채팅으로 모든 템플릿 확장 +- [Packages](/docs/build/packages) — 템플릿이 실제로 무엇인지 +- [Marketplace](/docs/build/marketplace) — 여러분의 포크 배포 +- [Quickstart](/docs/quickstart) — 개발자 경로의 전체 엔드투엔드 diff --git a/content/docs/build/views.ko.mdx b/content/docs/build/views.ko.mdx new file mode 100644 index 0000000..95f31e3 --- /dev/null +++ b/content/docs/build/views.ko.mdx @@ -0,0 +1,237 @@ +--- +title: 뷰 +description: List, Form, Kanban, Calendar, Gantt 등 — Console의 모든 오브젝트 표면을 선언하는 방법. +--- + +# 뷰 + +**뷰**는 사용자가 Console에서 레코드를 보고 편집하는 방식입니다. 뷰는 +선언적 메타데이터이며 — 오브젝트와 동일한 라이프사이클을 가집니다. 한 번 +선언하고, 패키지와 함께 배포하며, 어디서나 렌더링합니다. + +두 가지 종류: + +- **List 뷰** — 여러 레코드를 시각화합니다 (grid, kanban, calendar 등) +- **Form 뷰** — 단일 레코드를 보거나 편집합니다 (simple, tabbed, + wizard 등) + +스키마 소스: +[`packages/spec/src/ui/view.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/ui/view.zod.ts). + +## 가능한 가장 짧은 선언 + +모든 오브젝트는 뷰를 선언하지 않아도 기본 grid list와 simple form을 +자동으로 가집니다. 재정의하거나 확장하려면 `view`를 추가하세요: + +```ts +import { defineObject, F, P } from '@objectstack/spec' + +export default defineObject({ + name: 'support_ticket', + fields: [ /* ... */ ], + + view: { + list: { type: 'grid', columns: ['subject', 'status', 'priority', 'assignee'] }, + form: { sections: [ { label: 'Details', fields: ['subject','description','priority','status','assignee'] } ] } + } +}) +``` + +여러 뷰를 가진 오브젝트(예: 동일한 데이터에 대해 Kanban *과* calendar)의 +경우 명명된 변형을 사용하세요: + +```ts +view: { + listViews: { + by_status: { type: 'kanban', kanban: { groupByField: 'status' }, columns: ['subject','priority'] }, + schedule: { type: 'calendar', calendar: { startDateField: 'due_at', titleField: 'subject' } }, + by_owner: { type: 'grid', columns: ['subject','status','priority'], filterableFields: ['assignee'] } + }, + formViews: { + quick: { type: 'modal', sections: [ /* ... */ ] }, + full: { type: 'tabbed', sections: [ /* ... */ ] } + } +} +``` + +## List 뷰 타입 + +| `type` | 렌더링 | 필수 설정 | +|:--|:--|:--| +| `grid` | 데이터 테이블 (기본값) | `columns` | +| `kanban` | 컬럼이 있는 보드 | `kanban: { groupByField }` | +| `gallery` | 카드 덱 | `gallery: { coverField, titleField }` | +| `calendar` | 월 / 주 / 일 | `calendar: { startDateField, titleField }` | +| `timeline` | 시간순 피드 | `timeline: { startDateField, titleField }` | +| `gantt` | 프로젝트 타임라인 + 의존성 | `gantt: { startDateField, endDateField, titleField }` | +| `map` | 지리공간 핀 | `map: { locationField }` | +| `chart` | 임베드 차트 | `chart: { chartType, xAxisField, yAxisFields }` | + +### 공통 list 옵션 + +```ts +{ + type: 'grid', + columns: ['subject','status','priority','assignee','created_at'], + + filter: [ { field: 'archived', operator: 'equals', value: false } ], + sort: [ { field: 'created_at', order: 'desc' } ], + pagination: { pageSize: 25 }, + searchableFields: ['subject','description'], + filterableFields: ['status','priority','assignee'], + + navigation: { mode: 'drawer' }, // 'page' | 'drawer' | 'modal' | 'split' | 'popover' | 'new_window' | 'none' + selection: { type: 'multiple' }, // 'none' | 'single' | 'multiple' + + rowActions: ['close_ticket','assign_to_me'], + bulkActions: ['bulk_close','bulk_export'], + + conditionalFormatting: [ + { condition: P`record.priority == 'urgent'`, style: { background: '#fef2f2', fontWeight: 600 } } + ], + + exportOptions: ['csv','xlsx'], + emptyState: { title: 'No tickets yet', message: 'Create one to get started', icon: 'inbox' } +} +``` + +`filter`와 `sort`는 [ObjectQL](/docs/reference/objectql)로 컴파일됩니다. +`rowActions`와 `bulkActions`는 [actions](./actions)를 이름으로 참조합니다. + +### Kanban + +```ts +{ + type: 'kanban', + kanban: { + groupByField: 'status', // discrete field — usually a select + summarizeField: 'amount', // optional total per column + columns: ['subject', 'priority', 'assignee'] // fields shown on each card + } +} +``` + +보드의 컬럼은 `groupByField`의 값에서 가져오며, 그 순서와 색상은 해당 +필드의 `select` 옵션 정의에서 가져옵니다. + +컬럼 간 드래그 앤 드롭은 그룹화된 필드를 설정하는 업데이트를 발생시키며 — +수동 편집과 동일한 권한 규칙이 적용됩니다. + +### Calendar + +```ts +{ + type: 'calendar', + calendar: { + startDateField: 'start_at', + endDateField: 'end_at', // optional — single-point if omitted + titleField: 'subject', + colorField: 'priority' // optional — colours events by value + } +} +``` + +### Gantt + +```ts +{ + type: 'gantt', + gantt: { + startDateField: 'start_at', + endDateField: 'due_at', + titleField: 'name', + progressField: 'percent_complete', // optional, drives the progress bar + dependenciesField: 'depends_on' // optional — multiselect lookup to same object + } +} +``` + +### Chart (인라인) + +```ts +{ + type: 'chart', + chart: { + chartType: 'bar', // 'bar' | 'line' | 'pie' | 'area' | 'scatter' + xAxisField: 'created_at', + yAxisFields: ['amount'], + aggregation: 'sum', + groupByField: 'status' + } +} +``` + +Chart 뷰는 단일 오브젝트의 인라인 대시보드를 위한 것입니다 — 더 풍부한 +오브젝트 간 분석을 위해서는 전용 reports 표면을 사용하세요. + +## Form 뷰 타입 + +| `type` | 레이아웃 | +|:--|:--| +| `simple` | 단일 컬럼 또는 섹션 (기본값) | +| `tabbed` | 탭 섹션 | +| `wizard` | 단계별 플로우 | +| `split` | 마스터-디테일 두 개 창 | +| `drawer` | 측면 패널 폼 | +| `modal` | 다이얼로그 폼 | + +```ts +{ + type: 'tabbed', + sections: [ + { label: 'Overview', fields: ['subject','status','priority','assignee'] }, + { label: 'Customer', fields: ['customer','contact','email','phone'] }, + { label: 'Resolution', fields: ['resolution_notes','resolved_at'] } + ], + submitBehavior: { kind: 'next-record' } // 'thank-you' | 'redirect' | 'continue' | 'next-record' +} +``` + +### 공개 폼 + +폼은 익명 접근이 가능하도록 만들 수 있습니다: + +```ts +{ + type: 'simple', + sections: [ { fields: ['name','email','message'] } ], + sharing: { + enabled: true, + publicLink: 'contact-us', // slug under /forms/ + allowAnonymous: true + }, + submitBehavior: { kind: 'thank-you' } +} +``` + +이는 `GET /api/v1/forms/contact-us`와 +`POST /api/v1/forms/contact-us/submit`를 자동으로 노출합니다 — 인증이 +필요하지 않은 공개 폼 라우트입니다. [REST API → Public forms](/docs/reference/rest-api#public-forms)를 참고하세요. + +## 가시성, ARIA, 테마 + +- `visibleOn: P\`...\`` — form **섹션** 및 **필드**에서, 사용자 / 레코드 / + 환경에 따라 숨깁니다 (CEL 술어) +- `aria: { label, description, ... }` — 스크린 리더를 위한 ARIA 속성 + (list 및 form 뷰) +- `appearance: { showDescription, allowedVisualizations: [...] }` — list + 뷰에서, 최종 사용자가 전환할 수 있는 list 타입을 제한합니다 + +## 채팅으로 빌드하기 + +일반적으로 뷰 메타데이터를 직접 작성하지 않습니다. AI Builder에게 알려주세요: + +> *"subject와 priority를 보여주는 카드와 함께 status별로 그룹화된 +> support_ticket 오브젝트의 kanban 뷰를 추가해줘. status에 따라 컬럼을 +> 빨강 / 호박색 / 초록으로 색칠해줘."* + +AI Builder가 메타데이터 도구를 호출하고, diff를 큐에 넣으며, 승인하면 해당 +뷰가 Console에 나타납니다. [AI Builder](./ai-builder)를 참고하세요. + +## 참고 + +- [Data model](./data-model) — 뷰가 읽는 오브젝트 +- [Actions](./actions) — `rowActions` / `bulkActions`가 참조하는 것 +- [ObjectQL](/docs/reference/objectql) — `filter` / `sort`가 컴파일되는 대상 +- [CEL](/docs/reference/cel) — `conditionalFormatting`, `visibleOn` +- [`@objectstack/spec/ui/view.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/ui/view.zod.ts) — 스키마 diff --git a/content/docs/configure/ai.ko.mdx b/content/docs/configure/ai.ko.mdx new file mode 100644 index 0000000..e4fb612 --- /dev/null +++ b/content/docs/configure/ai.ko.mdx @@ -0,0 +1,165 @@ +--- +title: AI 서비스 +description: LLM, 임베더, RAG, MCP — 여러 공급자 간에 교체 가능하며 런타임에 전환할 수 있습니다. +--- + +# AI 서비스 + +ObjectOS는 AI를 **세 가지 교체 가능한 계층**으로 구성된 일급 기능으로 취급합니다: + +| 계층 | 패키지 | 역할 | +|---|---|---| +| **채팅 / 생성** | `@objectstack/service-ai` | 대화, 도구 호출, 스트리밍 | +| **임베딩** | `@objectstack/embedder-openai` (OpenAI 호환) | 텍스트 → 벡터, 시맨틱 검색 및 RAG용 | +| **지식 / RAG** | `@objectstack/service-knowledge` + 어댑터 | 문서 → 인덱싱된 지식 베이스 | + +세 계층 모두 선택 사항이며, 모두 공급자에 종속되지 않고, 모두 **Console → Configuration**에서 재시작 없이 런타임에 재구성할 수 있습니다. + +## 채팅 / 생성 + +[Vercel AI SDK](https://ai-sdk.dev)로 구동됩니다. 원하는 공급자를 peer 의존성으로 설치하세요: + +```bash +pnpm add @ai-sdk/openai # OpenAI +pnpm add @ai-sdk/anthropic # Claude +pnpm add @ai-sdk/google # Gemini +pnpm add @ai-sdk/gateway # AI gateway / OpenRouter / proxies +``` + +그런 다음 Vercel-SDK 어댑터와 함께 AI 서비스 플러그인을 등록합니다: + +```ts +import { AIServicePlugin, VercelLLMAdapter } from '@objectstack/service-ai'; +import { openai } from '@ai-sdk/openai'; + +kernel.use(new AIServicePlugin({ + adapter: new VercelLLMAdapter({ model: openai('gpt-4o') }), + models: [ + { id: 'fast', provider: 'openai', model: 'gpt-4o-mini' }, + { id: 'smart', provider: 'openai', model: 'gpt-4o' }, + ], + defaultModelId: 'fast', +})); +``` + +`models` 목록은 런타임 모델 레지스트리에 입력됩니다 — 이는 기본 모델 해석과 트레이스 내 비용 귀속을 주도합니다. 기본적으로 플러그인은 `ai` 설정 네임스페이스에도 바인딩되며, 운영자가 Console에서 공급자/자격 증명/모델을 편집하면 어댑터를 실시간으로 다시 빌드하므로 재시작이 필요하지 않습니다. + +공급자 API 키는 각 SDK의 일반 환경 변수에서 가져옵니다: + +| 공급자 | 환경 변수 | +|---|---| +| OpenAI | `OPENAI_API_KEY` | +| Anthropic | `ANTHROPIC_API_KEY` | +| Google | `GOOGLE_GENERATIVE_AI_API_KEY` | +| AI Gateway | `AI_GATEWAY_API_KEY` | + +Console에서는 이 값들을 런타임 설정으로 붙여넣을 수도 있습니다 — 동일한 우선순위(env > settings)를 따르지만, 실시간 편집이 가능하므로 재시작이 필요 없습니다. + +### 코드에서 AI 서비스 사용하기 + +```ts +const ai = kernel.getService('ai'); + +// One-shot chat +const result = await ai.chat( + [{ role: 'user', content: 'Summarize ObjectStack in two sentences.' }], + { model: 'smart' }, +); + +// Structured output +const { object } = await ai.generateObject( + [{ role: 'user', content: 'Classify this ticket: ...' }], + { schema, model: 'fast' }, +); +``` + +이 서비스는 `complete()`, `streamChat()`, `embed()`, `listModels()`도 노출합니다. 대화는 `ai_conversations` / `ai_messages` 레코드로 영속화되며 REST API(`POST /api/v1/ai/chat`, `POST /api/v1/ai/conversations`)를 통해 관리됩니다. + +### 에이전트와 플로우에서 사용하기 + +선언적 AI — 에이전트, 스킬, 도구 — 에 대해서는 [AI Agents](/docs/build/agents)를 참고하세요. 플로우는 등록된 모든 액션(`ai.chat()` / `ai.generateObject()` 호출을 감싸는 액션 포함)을 `action` 노드로 호출할 수 있습니다. 주변 맥락은 [Flows & Automation](/docs/build/flows)을 참고하세요. + +## 임베더 + +임베더는 텍스트를 밀집 벡터로 변환합니다. 단일 OpenAI 호환 어댑터인 `@objectstack/embedder-openai`는 OpenAI는 물론 OpenAI 호환 `/v1/embeddings` 엔드포인트를 노출하는 모든 공급자를 지원합니다 — **preset**으로 하나를 선택하거나(이 경우 base URL이 자동 설정됨), `baseUrl`을 사용자 정의 엔드포인트로 지정하세요. + +| 공급자 | `preset` | 비고 | +|---|---|---| +| OpenAI | `openai` | `text-embedding-3-small`/`-large` | +| Azure OpenAI | `azure` | `baseUrl`로 전체 배포 URL 제공 | +| 阿里通义 DashScope | `dashscope` | `text-embedding-v3` | +| 智谱 GLM | `zhipu` | `embedding-2` | +| 硅基流动 SiliconFlow | `siliconflow` | OSS 모델 애그리게이터 | +| 火山 Doubao | `doubao` | ByteDance | +| MiniMax | `minimax` | — | +| Ollama (자체 호스팅) | `ollama` | 오프라인(air-gapped) 환경에 적합 (`http://localhost:11434/v1`) | +| 사용자 정의 | _(생략; `baseUrl` 설정)_ | 자체 OpenAI 호환 엔드포인트 사용 | + +코드에서 구성: + +```ts +import { createOpenAIEmbedder } from '@objectstack/embedder-openai'; + +const embedder = createOpenAIEmbedder({ + preset: 'openai', + model: 'text-embedding-3-small', + // apiKey from OPENAI_API_KEY env if omitted +}); +``` + +또는 **Console → Configuration → AI → Embedder**에서 런타임에 선택하세요. 재시작 없이 공급자를 전환할 수 있으며, 기존 벡터는 계속 검색 가능합니다(백그라운드에서 다시 인덱싱할 수 있습니다). + +## 지식 / RAG + +지식 서비스는 문서 수집, 청킹, 임베딩(임베더 서비스 경유), 검색을 오케스트레이션합니다. 실제 저장 및 검색 백엔드는 교체 가능합니다: + +| 어댑터 | 백엔드 | 적합한 용도 | +|---|---|---| +| `@objectstack/knowledge-memory` | 인프로세스 | 개발, 데모, 소규모 KB | +| `@objectstack/knowledge-ragflow` | [RAGFlow](https://github.com/infiniflow/ragflow) | 청킹 + 리랭킹을 갖춘 고품질 OSS RAG | + +```ts +import { KnowledgeServicePlugin } from '@objectstack/service-knowledge'; +import { KnowledgeRagflowPlugin } from '@objectstack/knowledge-ragflow'; + +kernel.use(new KnowledgeServicePlugin({ defaultTopK: 10 })); +kernel.use(new KnowledgeRagflowPlugin({ + endpoint: process.env.RAGFLOW_ENDPOINT, // e.g. http://localhost:9380 + apiKey: process.env.RAGFLOW_API_KEY, +})); +``` + +인덱싱된 지식 베이스는 일급 객체가 됩니다 — 플로우에서 쿼리하고, Console에 노출하고, 검색 컨텍스트로 AI 어시스턴트에 연결할 수 있습니다. + +## MCP — Model Context Protocol + +ObjectOS는 개방형 [Model Context Protocol](https://modelcontextprotocol.io)을 통해 자신을 AI 에이전트(Claude Desktop, IDE, 사용자 정의 에이전트)에 대한 도구 서버로 노출할 수 있습니다. + +```ts +import { MCPServerPlugin } from '@objectstack/plugin-mcp-server'; + +kernel.use(new MCPServerPlugin({ + transport: 'stdio', // or 'http' + autoStart: true, +})); +``` + +에이전트는 MCP를 통해 ObjectOS 도구를 발견하고 호출합니다 — 호출하는 사용자의 권한이 적용됩니다. 서버는 `list_objects`, `describe_object`, `query_records`, `get_record`, `aggregate_data`와 같은 범용 도구를 포함하여 AI 서비스의 도구 레지스트리를 브리지합니다. + +## 운영 보장 + +- **필수 클라우드 의존성 없음.** 채팅에 Ollama + Ollama 임베더 + 메모리 지식을 사용하면 완전히 오프라인(air-gapped)으로 동작합니다. +- **실시간 교체 가능.** Console에서 공급자를 변경하면 새 요청은 다음 호출부터 새 공급자를 사용합니다. 재시작이 필요 없습니다. +- **테넌트별 구성.** 각 Environment는 자체 AI 설정을 가집니다. 테넌트 A는 OpenAI, 테넌트 B는 Anthropic — 동일한 런타임에서 동작합니다. +- **감사 로그 기록.** 모든 대화, 도구 호출, 임베더 요청을 감사할 수 있습니다(`@objectstack/plugin-audit`). +- **비용 인식.** 토큰 수와 공급자 ID가 감사 로그로 전달되어 비용 청구/비용 분석에 활용됩니다. + +## 다음으로 갈 곳 + +- [AI Agents](/docs/build/agents) — 선언적 에이전트, 스킬, 도구 +- [Flows & Automation](/docs/build/flows) — 선언적 비즈니스 로직에서 AI 호출하기 +- [Marketplace](/docs/build/marketplace) — 기본 카탈로그의 AI 기반 앱 +- [Security & Compliance](/docs/reference/security) — AI 데이터 흐름이 격리되는 방식 +- [`@objectstack/service-ai` source](https://github.com/objectstack-ai/framework/tree/main/packages/services/service-ai) +- [`@objectstack/embedder-openai` source](https://github.com/objectstack-ai/framework/tree/main/packages/plugins/embedder-openai) +- [`@objectstack/service-knowledge` source](https://github.com/objectstack-ai/framework/tree/main/packages/services/service-knowledge) diff --git a/content/docs/configure/api-access.ko.mdx b/content/docs/configure/api-access.ko.mdx new file mode 100644 index 0000000..df84734 --- /dev/null +++ b/content/docs/configure/api-access.ko.mdx @@ -0,0 +1,102 @@ +--- +title: API 액세스 +description: 통합을 위한 생성된 REST API, 인증 및 API 키. +--- + +# API 액세스 + +아티팩트에 선언된 모든 오브젝트는 생성된 REST API를 통해 노출됩니다. +동일한 엔드포인트가 UI, 통합, 고객 ETL 스크립트를 모두 구동하므로 +별도로 유지 관리해야 할 "통합 API"가 없습니다. + +## 기본 URL + +```text +https:///api/v1 +``` + +주요 엔드포인트: + +| 경로 | 용도 | +|---|---| +| `/api/v1/data/` | 각 오브젝트에 대해 생성된 CRUD (예: `/api/v1/data/account`) | +| `/api/v1/data//{id}` | 단일 레코드 읽기/업데이트/삭제 | +| `/api/v1/actions//` | 선언적 액션 호출 (POST) | +| `/api/v1/auth/*` | 인증 (로그인, 세션, OAuth, OIDC) | +| `/api/v1/meta/*` | 활성화된 경우 메타데이터 API (오브젝트, 필드, 뷰) | +| `/api/v1/health` | 활성/준비 상태 프로브 (인증 없음) | + +접두사는 기본적으로 `/api/v1`(API `basePath`인 `/api`에 `version`인 `v1`을 +더한 값)이며 ObjectOS 스택에서 구성할 수 있습니다. + +## 인증 옵션 + +호출자는 세 가지 방식으로 인증할 수 있습니다. + +| 방식 | 적합한 용도 | 방법 | +|---|---|---| +| 세션 쿠키 | 브라우저/UI 트래픽 | `/api/v1/auth/*`를 통해 로그인하며, 쿠키는 프로젝트 호스트명으로 범위가 지정됩니다 | +| Bearer 액세스 토큰 | 모바일, SPA, 단기 서버 작업 | `/api/v1/auth/sign-in/email`에서 자격 증명을 교환하고 `Authorization: Bearer `을 전달합니다 | +| API 키 | 서버 간 통신, ETL, 장기 통합 | `sys_api_key`를 생성하고 bearer 토큰으로 전달합니다 | + +세 가지 방식 모두 동일한 `AuthPlugin`을 거쳐 `sys_user` 컨텍스트로 +확인되며, `SecurityPlugin`이 이를 권한 및 레코드 액세스에 대해 평가합니다. + +## API 키 + +API 키는 일급 `sys_api_key` 레코드입니다. 키 값 자체는 해시되어 +저장되며 `prefix`와 메타데이터만 쿼리할 수 있으므로, 유출된 키를 +데이터베이스에서 재구성할 수 없습니다. + +키를 발급하려면: + +1. 관리자 권한으로 **Console → API Keys**에 로그인합니다. +2. 소유 사용자를 선택합니다(API 호출은 해당 사용자의 권한 및 레코드 + 액세스를 상속합니다). +3. 선택적으로 만료일을 설정합니다. +4. 표시된 비밀 키를 **한 번** 복사합니다 — 다시 표시되지 않습니다. + +키를 bearer 토큰으로 사용합니다: + +```bash +curl https://app.example.com/api/v1/data/account \ + -H "Authorization: Bearer os_pk_live_…" +``` + +키를 취소하려면 해당 `sys_api_key` 레코드에서 `revoke_api_key` 액션을 +실행합니다(Console UI에서도 사용 가능). 취소는 다음 요청에서 즉시 +적용됩니다. + +## 페이지네이션, 필터링 및 정렬 + +생성된 목록 엔드포인트는 표준 ObjectStack 쿼리 매개변수를 허용합니다. + +| 매개변수 | 의미 | +|---|---| +| `?limit=` | 페이지 크기 (서버 측 상한 적용) | +| `?offset=` 또는 `?cursor=` | 페이지네이션 | +| `?filter=` | ObjectQL 필터 구문을 사용한 서버 측 필터링 | +| `?sort=` | 정렬 표현식 (예: `-created_at`) | +| `?fields=` | 희소 필드 선택 | + +전체 필터 문법은 프레임워크 문서를 참조하십시오 — 런타임 문법이 +소스 오브 트루스입니다. + +## 속도 제한 + +프레임워크의 `RateLimiter` 프리미티브(또는 인그레스 / API 게이트웨이)를 +사용하여 IP별 및 ID별 제한을 적용하십시오. 권장 시작 버킷은 +[Production readiness](/docs/operate/production)에 문서화되어 있습니다. + +## CORS + +호출자가 다른 출처의 브라우저에서 실행되는 경우, 인그레스 또는 +런타임의 미들웨어를 통해 CORS를 구성하십시오. 와일드카드 출처를 +자격 증명이 포함된 요청과 **결합하지 마십시오**. + +## OpenAPI / 디스커버리 + +이미지에 해당 기능이 포함된 경우, 런타임은 생성된 REST API에 대한 +OpenAPI 문서를 제공할 수 있습니다. 오브젝트 이름과 생성된 라우트는 +배포된 아티팩트를 따르므로, 고객 통합은 URL을 직접 작성하는 대신 +배포별 OpenAPI 문서에서 클라이언트를 생성해야 합니다. diff --git a/content/docs/configure/authentication.ko.mdx b/content/docs/configure/authentication.ko.mdx new file mode 100644 index 0000000..285b1d8 --- /dev/null +++ b/content/docs/configure/authentication.ko.mdx @@ -0,0 +1,124 @@ +--- +title: 인증 +description: 로그인, 세션, OAuth, OIDC/SSO 및 디바이스 플로우를 구성합니다. +--- + +# 인증 + +ObjectOS는 Better Auth로 구동되는 ObjectStack 인증 플러그인을 사용합니다. +인증은 프로젝트 로컬 방식입니다. 각 프로젝트는 자체 ID 테이블과 +세션 범위를 가집니다. + +## 지원 기능 + +패키징된 애플리케이션과 활성화된 설정에 따라 ObjectOS는 다음을 +지원할 수 있습니다. + +- 이메일/비밀번호 로그인; +- 세션 관리; +- 비밀번호 재설정 및 이메일 인증; +- Google, GitHub, Microsoft, Apple과 같은 소셜 OAuth 제공자; +- Okta, Entra ID, Keycloak, Ping과 같은 엔터프라이즈 OIDC/SSO; +- 2단계 인증; +- 패스키/WebAuthn; +- 매직 링크; +- CLI/브라우저 디바이스 플로우. + +## 필수 시크릿 + +다음을 설정하세요. + +```bash +OS_AUTH_SECRET=replace-with-a-strong-random-secret +``` + +ObjectOS는 이 값과 프로젝트 환경 id로부터 안정적인 프로젝트별 시크릿을 +파생합니다. 이는 다음을 의미합니다. + +- 세션이 컨테이너 재시작 후에도 유지됩니다; +- 한 프로젝트의 토큰을 다른 프로젝트에서 재사용할 수 없습니다; +- `OS_AUTH_SECRET`을 교체하면 세션이 무효화됩니다. + +레거시 `AUTH_SECRET` 별칭을 포함한 인증 관련 설정의 전체 목록은 +[Environment variables](/docs/reference/environment-variables)를 +참조하세요. + +## 세션 격리 + +다중 프로젝트 배포에서 쿠키는 프로젝트 호스트명으로 범위가 지정됩니다. +ObjectOS는 프로젝트 세션에 대해 광범위한 루트 도메인 쿠키를 의도적으로 +피합니다. 이는 세션이 고객 프로젝트 간에 유출되는 것을 방지합니다. + +## 소셜 로그인 + +애플리케이션 패키지가 인증 구성을 노출하는 방식에 따라 환경 변수 또는 +시스템 설정을 통해 제공자 자격 증명을 구성하세요. + +제공자 콜백 URL은 제공자 유형에 따라 다릅니다. ObjectStack은 두 가지 +서로 다른 콜백 경로를 노출하며 이들은 서로 **호환되지 않습니다**. + +| 제공자 유형 | 콜백 경로 | +|---|---| +| 내장 소셜 (Google, GitHub, Microsoft, Apple, …) | `/api/v1/auth/callback/` | +| 일반 OIDC / OAuth2 (Okta, Entra ID, Keycloak, Ping, …) | `/api/v1/auth/oauth2/callback/` | + +예시: + +```text +https://crm.example.com/api/v1/auth/callback/google +https://crm.example.com/api/v1/auth/callback/microsoft +https://crm.example.com/api/v1/auth/oauth2/callback/okta +https://crm.example.com/api/v1/auth/oauth2/callback/entra +``` + +ObjectOS에서 제공자를 활성화하기 전에 ID 제공자의 애플리케이션 +등록에서 일치하는 리디렉션 URI를 구성하세요. + +## 엔터프라이즈 OIDC/SSO + +OIDC 제공자는 일반 OAuth2 제공자로 등록되며 +`/api/v1/auth/oauth2/callback/` 경로를 사용합니다. 일반적인 +구성에는 다음이 필요합니다. + +| 필드 | 설명 | +|---|---| +| Provider id | `okta` 또는 `entra`와 같은 안정적인 id (콜백 URL에 사용됨) | +| Display name | 사용자에게 표시되는 버튼 레이블 | +| Discovery URL | `.well-known/openid-configuration` 엔드포인트 | +| Client id | ID 제공자의 애플리케이션 클라이언트 id | +| Client secret | 환경 또는 암호화된 설정에 저장되는 시크릿 | +| Scopes | 일반적으로 `openid email profile` | + +고객 배포의 경우 수동으로 구성된 authorization/token/userinfo +엔드포인트보다 OIDC 디스커버리 URL을 선호하세요. + +## 플랫폼 SSO + +클라우드에 연결된 배포에서 ObjectOS는 컨트롤 플레인 로그인을 플랫폼 +SSO 제공자로 사용할 수 있습니다. 이미 컨트롤 플레인에 로그인한 빌더는 +별도의 프로젝트 로컬 계정을 생성하지 않고도 프로젝트 런타임에 +프로비저닝될 수 있습니다. + +이를 위해서는 컨트롤 플레인과 ObjectOS가 동일한 `OS_AUTH_SECRET` 기본 +시크릿을 공유해야 합니다. 고객이 모든 프로젝트가 완전히 별도의 로그인 +경계를 소유하기를 원하는 경우에만 플랫폼 SSO를 비활성화하세요. + +## 운영 점검 + +프로덕션 전에: + +- 만료된 토큰이 `401`을 반환하는지 확인하세요; +- 로그아웃이 활성 세션을 취소하는지 확인하세요; +- 정책에서 요구하는 경우 비밀번호 재설정이 다른 세션을 취소하는지 + 확인하세요; +- 콜백 URL이 공개 프로젝트 도메인과 일치하는지 확인하세요; +- 신뢰할 수 있는 출처에 승인된 도메인만 포함되는지 확인하세요; +- `OS_AUTH_SECRET`이 소스가 아닌 시크릿 관리자에 저장되는지 + 확인하세요. + +## 다음 단계 + +인증은 사용자가 *누구*인지를 확립합니다. 로그인 후 사용자가 *무엇*에 +액세스할 수 있는지 제어하려면 [Permissions](/docs/configure/permissions)를 +참조하세요. 브라우저가 아닌 클라이언트 및 머신 간 액세스에 대해서는 +[API access](/docs/configure/api-access)를 참조하세요. diff --git a/content/docs/configure/data-sources.de.mdx b/content/docs/configure/data-sources.de.mdx new file mode 100644 index 0000000..7b9d607 --- /dev/null +++ b/content/docs/configure/data-sources.de.mdx @@ -0,0 +1,266 @@ +--- +title: Datasources +description: "Verbinden Sie ObjectOS mit Ihren bestehenden Geschäftsdatenbanken, routen Sie Objekte dorthin und lassen Sie AI die Daten abfragen — nativ." +--- + +# Datasources + +Eine **Datasource** ist eine benannte Verbindung zu einem externen +Datenspeicher. Indem Sie Datasources deklarieren, richten Sie ObjectOS auf +die Datenbanken aus, mit denen Ihr Unternehmen bereits arbeitet — ein +produktives PostgreSQL, ein Reporting-MySQL-Replikat, ein +MongoDB-Cluster — und binden dann Objekte daran. Sobald ein Objekt +gebunden ist, arbeitet alles andere in der Plattform (die +REST-/GraphQL-API, Berechtigungen, Flows, Dashboards und **AI-Agents**) +einheitlich gegen diese Daten, ohne sich darum zu kümmern, wo die Zeilen +physisch liegen. + +Dies ist einer der praktischsten Einführungspfade von ObjectOS: Statt ein +Legacy-System zu migrieren, verbinden Sie sich damit, modellieren die +Tabellen, die Sie interessieren, als Objekte und **erweitern es um +AI-native Fähigkeiten** — Chat, Analyse, Automatisierung — auf Daten, die +genau dort bleiben, wo sie sind. + +## Was eine Datasource ist + +Jede Datasource ist ein einfaches Objekt, das von `DatasourceSchema` +validiert wird. Die Kernfelder: + +| Feld | Zweck | +|---|---| +| `name` | Eindeutiger Bezeichner (`^[a-z_][a-z0-9_]*$`), den Objekte referenzieren | +| `label` | Menschenlesbarer Anzeigename | +| `driver` | Welcher Treiber die Verbindung verwaltet (`postgres`, `mysql`, `sqlite`, `mongodb`, `memory` oder ein per Plugin beigesteuerter Treiber) | +| `config` | Treiberspezifische Verbindungseinstellungen (Host, Datenbank, Anmeldedaten, …) | +| `pool` | Dimensionierung des Verbindungspools (`min`, `max`, Timeouts) | +| `readReplicas` | Optionale schreibgeschützte Replikat-Konfigurationen | +| `capabilities` | Überschreiben, was der Treiber als push-down-fähig ausweist | +| `healthCheck` | Intervall/Timeout der Liveness-Prüfung | +| `active` | Ob die Verbindung aktiviert ist | + +Die mitgelieferten Treiber sind: + +| Treiber-Paket | `driver` | Backends | +|---|---|---| +| `@objectstack/driver-sql` | `postgres`, `mysql`, `sqlite` | PostgreSQL, MySQL, SQLite (über knex) | +| `@objectstack/driver-mongodb` | `mongodb` | MongoDB | +| `@objectstack/driver-memory` | `memory` | In-Process (Tests, Demos) | +| `@objectstack/driver-sqlite-wasm` | `sqlite` (WASM) | SQLite in WebContainers / Browser | + +## Datasources deklarieren + +Datasources werden im Stack deklariert und mit `defineStack` +zusammengesetzt. Definieren Sie jede Verbindung als typisiertes +`Datasource`-Objekt und führen Sie sie dann unter `datasources` auf: + +```ts +// src/datasources/business.datasource.ts +import type { Datasource } from '@objectstack/spec'; + +// Mit einer BESTEHENDEN Produktionsdatenbank verbinden. Anmeldedaten kommen +// aus der Umgebung — niemals Secrets inline im Quellcode. +export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + port: Number(process.env.BIZ_DB_PORT ?? 5432), + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + pool: { min: 1, max: 10 }, + active: true, +}; +``` + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; +import { BusinessDb } from './src/datasources/business.datasource.js'; + +export default defineStack({ + manifest: { id: 'app.example.crm-extend', namespace: 'biz', version: '1.0.0' }, + datasources: [BusinessDb], + objects: Object.values(objects), +}); +``` + +> Es gibt **keinen** `defineDatasource()`-Helfer. Eine Datasource ist +> einfach ein `Datasource`-Objekt, das Sie im `datasources`-Array +> platzieren — genau so, wie es der `examples/app-crm`-Stack im +> Framework-Repo tut. + +## Objekte an eine Datasource binden + +Jedes Objekt hat ein `datasource`-Feld. Es ist standardmäßig auf +`'default'` (die primäre Datenbank) gesetzt. Setzen Sie es, um ein +bestimmtes Objekt auf eines Ihrer verbundenen Systeme zu routen: + +```ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Customer = ObjectSchema.create({ + name: 'biz_customer', + label: 'Customer', + datasource: 'business_primary', // ← Lese-/Schreibvorgänge gehen an die Business-DB + fields: { + name: Field.text({ label: 'Name', required: true }), + email: Field.text({ label: 'Email' }), + tier: Field.select({ label: 'Tier', options: [/* … */] }), + }, +}); +``` + +### Zentralisiertes Routing mit `datasourceMapping` + +Objekte einzeln zu binden ist für eine Handvoll in Ordnung. Für ganze +Namespaces oder Pakete deklarieren Sie Routing-Regeln einmal im Stack. +Regeln werden der Reihe nach ausgewertet (oder nach `priority`); die erste +Übereinstimmung gewinnt: + +```ts +export default defineStack({ + datasources: [BusinessDb, AnalyticsReplica], + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { objectPattern: 'report_*', datasource: 'analytics_replica' }, + { package: 'com.example.logs', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ], +}); +``` + +Eine Regel matcht nach `namespace`, `package`, `objectPattern` (Glob) oder +`default` und benennt die Ziel-`datasource`. So bleiben Entscheidungen zur +Datenresidenz an einem Ort, statt über die Objektdateien verstreut zu sein. + +## Objekte aus bestehenden Tabellen generieren + +Sie müssen nicht für jede Tabelle ein Objekt von Hand schreiben. Der +schnellste Weg, ein Legacy-Schema heute in ObjectOS zu bringen, besteht +darin, **einen Coding-Agent (Claude Code) die Business-Tabellen scannen und +Objektdefinitionen auf Quellcode-Ebene generieren zu lassen** — eine +`*.object.ts`-Datei pro Tabelle, genau in der Form, die das Framework +erwartet. + +Die Referenz-App [`hotcrm`](https://github.com/objectstack-ai/hotcrm) ist +das kanonische Beispiel für diese Form: Jede Tabelle ist eine +`src/objects/.object.ts`-Datei, die `ObjectSchema.create({ … })` mit +`Field.*`-Definitionen verwendet, alles zusammengesetzt von `defineStack`. +Ein typischer Ablauf: + +1. **Verbinden** Sie die Business-Datenbank als Datasource (siehe oben). +2. **Richten Sie Claude Code auf das Schema.** Bitten Sie es, die + verbundene Datenbank zu introspektieren — Tabellennamen, Spalten, Typen, + Fremdschlüssel — und eine `ObjectSchema.create`-Datei pro Tabelle zu + generieren, wobei SQL-Spalten auf `Field.*`-Typen und Fremdschlüssel auf + `Field.lookup(...)` abgebildet werden. Setzen Sie die `datasource` jedes + Objekts auf Ihre Verbindung (oder verlassen Sie sich auf + `datasourceMapping`). +3. **Prüfen & verfeinern** Sie die generierten Objekte — fügen Sie Labels, + Feldgruppen, Validierungen und Berechtigungen hinzu. Die Ausgabe ist + gewöhnlicher Quellcode, der Ihnen gehört und den Sie committen, genau wie + `hotcrm/src/objects/*.object.ts`. +4. **Ausführen.** Die Objekte lesen und schreiben jetzt Ihre bestehenden + Tabellen über die gebundene Datasource. + +Da die generierten Objekte einfacher Quellcode sind, haben Sie volle +Kontrolle: Behalten Sie, was passt, lassen Sie Spalten weg, die Sie nicht +offenlegen wollen, und legen Sie ObjectOS-Funktionen (History-Tracking, +Aktivitäten, Sharing-Regeln) auf einer Datenbank ab, die die Plattform nie +besitzen musste. + +## Capability-bewusste Abfrage-Pushdown + +Jede Datasource weist `DatasourceCapabilities` aus — ob sie Filter, +Sortierung, Pagination, Aggregationen, Joins, Volltextsuche, Transaktionen +und mehr verarbeiten kann. ObjectQL nutzt dies, um zu entscheiden, was an +die Datenbank **heruntergeschoben** wird und was im Speicher ausgewertet +wird: + +| Capability | Effekt bei Unterstützung | +|---|---| +| `queryFilters` | `WHERE`-Klauseln laufen in der Datenbank | +| `querySorting` / `queryPagination` | `ORDER BY` / `LIMIT` laufen serverseitig | +| `queryAggregations` | `GROUP BY` / Aggregate laufen serverseitig | +| `joins` | Joins verwandter Objekte laufen serverseitig | +| `fullTextSearch` | Suche trifft einen nativen Index | +| `readOnly` | Die Verbindung lehnt Schreibvorgänge ab | + +Eine leistungsfähige SQL-Datasource schiebt nahezu alles herunter; eine +eingeschränkte oder schreibgeschützte Quelle liefert dieselben +Abfrageergebnisse, nur mit mehr Arbeit in der Engine. Sie können den +ausgewiesenen Satz pro Datasource über das `capabilities`-Feld +überschreiben, wenn Sie es besser wissen als die Treiber-Vorgabe. + +## AI über verbundenen Daten + +Sobald Tabellen als Objekte modelliert sind, **arbeitet die AI-Schicht +kostenlos auf ihnen**. Die Agents und Tools von ObjectOS — +`list_objects`, `describe_object`, `query_records`, `aggregate_data` und +der Data-Chat-Agent — laufen alle über ObjectQL, das jedes Objekt zu seiner +gebundenen Datasource routet. Das bedeutet: + +- Ein Benutzer kann **Fragen in natürlicher Sprache** zu Daten stellen, die + im Legacy-Business-System liegen, und die Antwort wird gegen die echten + Zeilen berechnet. +- Tool-Calls und Abfragen respektieren die **Berechtigungen des + aufrufenden Benutzers** — AI sieht nie mehr, als der angemeldete Benutzer + sehen darf. +- Dieselben Agents, Flows und Dashboards funktionieren, egal ob ein Objekt + von der primären Datenbank oder einem externen Business-System gestützt + wird. + +Siehe [AI-Service](/docs/configure/ai) und +[AI Agents](/docs/build/agents) dafür, wie Sie die Chat- und +Agent-Schichten verdrahten, und [Runtime](/docs/configure/runtime) für die +Konfiguration der primären Datenbank, die die `default`-Datasource stützt. + +## Sicherheitshinweise + +- **Niemals Anmeldedaten inline.** Beziehen Sie Host/User/Passwort aus + Umgebungsvariablen (oder einem Secrets-Manager), wie oben gezeigt. +- **Verwenden Sie schreibgeschützte Verbindungen** für Systeme, in die Sie + nicht schreiben wollen — setzen Sie die `readOnly`-Capability der Quelle + (oder einen schreibgeschützten DB-Benutzer), damit ein versehentlicher + Schreibvorgang die Produktion nicht erreichen kann. +- **Mit Berechtigungen eingrenzen.** Objekt- und feldbezogene + Berechtigungen gelten für verbundene Daten genauso wie für native + Objekte. + +## Roadmap: In-Product External Datasource Federation + +Der obige Ablauf — eine Datenbank verbinden, Objekte modellieren, sie mit +einem Coding-Agent generieren — funktioniert heute mit den mitgelieferten +Bausteinen. Eine reichhaltigere, **schlüsselfertige +Federation**-Erfahrung befindet sich unter +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +in aktiver Konzeption (Status: *Proposed*). Geplant, **noch nicht +ausgeliefert**: + +- Ein `schemaMode` (`managed` / `external` / `validate-only`), sodass + ObjectOS sich an Tabellen binden kann, die es **nicht** besitzt, ohne zu + versuchen, sie zu migrieren. +- Ein `external`-Bindungs-Unterdatensatz auf Objekten, der Objektfelder auf + bestehende Spalten abbildet. +- Ein `os datasource introspect` / `validate` CLI, um ein Schema zu + importieren und Objekte in einem Schritt zu scaffolden. +- **Sicherheits-Gates** zur Boot- und Schreibzeit für extern besessene + Schemata sowie ein Studio-Assistent für den gesamten Ablauf. + +Bis diese verfügbar sind, bevorzugen Sie den dokumentierten Pfad: +deklarieren Sie die Datasource, binden Sie Objekte (generiert oder von Hand +geschrieben) und verifizieren Sie zuerst gegen eine Nicht-Produktionskopie. + +## Wie es weitergeht + +- [Runtime](/docs/configure/runtime) — die primäre Datenbank hinter `default` +- [AI-Service](/docs/configure/ai) — Chat, Embeddings, RAG, MCP +- [AI Agents](/docs/build/agents) — deklarative Agents über Ihren Objekten +- [Objects](/docs/build/objects) — die `ObjectSchema.create`-Autorenoberfläche +- [`examples/app-crm` Datasources](https://github.com/objectstack-ai/framework/tree/main/examples/app-crm/src/datasources) — ein echtes Beispiel für Datasource + Routing diff --git a/content/docs/configure/data-sources.es.mdx b/content/docs/configure/data-sources.es.mdx new file mode 100644 index 0000000..1b44cc5 --- /dev/null +++ b/content/docs/configure/data-sources.es.mdx @@ -0,0 +1,259 @@ +--- +title: Fuentes de datos +description: Conecta ObjectOS a tus bases de datos de negocio existentes, enruta objetos hacia ellas y deja que la IA consulte los datos — de forma nativa. +--- + +# Fuentes de datos + +Un **datasource** es una conexión con nombre a un almacén de datos externo. +Al declarar datasources, apuntas ObjectOS hacia las bases de datos sobre las +que tu negocio ya funciona — un PostgreSQL de producción, una réplica MySQL +de reporting, un clúster MongoDB — y luego vinculas objetos a ellas. Una vez +que un objeto está vinculado, todo lo demás en la plataforma (la API +REST/GraphQL, los permisos, los flows, los dashboards y los **agents de IA**) +trabaja sobre esos datos de manera uniforme, sin importar dónde residen +físicamente las filas. + +Esta es una de las vías de adopción más prácticas de ObjectOS: en lugar de +migrar un sistema heredado, te conectas a él, modelas como objetos las tablas +que te interesan y lo **extiendes con capacidades nativas de IA** — chat, +análisis, automatización — sobre datos que permanecen exactamente donde +están. + +## Qué es un datasource + +Cada datasource es un objeto sencillo validado por `DatasourceSchema`. Los +campos principales: + +| Campo | Propósito | +|---|---| +| `name` | Identificador único (`^[a-z_][a-z0-9_]*$`) que los objetos referencian | +| `label` | Nombre legible para mostrar | +| `driver` | Qué driver gestiona la conexión (`postgres`, `mysql`, `sqlite`, `mongodb`, `memory`, o un driver aportado por un plugin) | +| `config` | Ajustes de conexión específicos del driver (host, base de datos, credenciales, …) | +| `pool` | Dimensionamiento del pool de conexiones (`min`, `max`, tiempos de espera) | +| `readReplicas` | Configuraciones opcionales de réplicas de solo lectura | +| `capabilities` | Sobrescribe lo que el driver anuncia que puede delegar (push down) | +| `healthCheck` | Intervalo/tiempo de espera de la sonda de actividad | +| `active` | Si la conexión está habilitada | + +Los drivers incluidos son: + +| Paquete del driver | `driver` | Backends | +|---|---|---| +| `@objectstack/driver-sql` | `postgres`, `mysql`, `sqlite` | PostgreSQL, MySQL, SQLite (vía knex) | +| `@objectstack/driver-mongodb` | `mongodb` | MongoDB | +| `@objectstack/driver-memory` | `memory` | En proceso (tests, demos) | +| `@objectstack/driver-sqlite-wasm` | `sqlite` (WASM) | SQLite en WebContainers / navegador | + +## Declarar datasources + +Los datasources se declaran en el stack y se ensamblan con `defineStack`. +Define cada conexión como un objeto `Datasource` tipado y luego enuméralos +bajo `datasources`: + +```ts +// src/datasources/business.datasource.ts +import type { Datasource } from '@objectstack/spec'; + +// Conéctate a una base de datos de producción EXISTENTE. Las credenciales +// provienen del entorno — nunca incluyas secretos en línea en el código. +export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + port: Number(process.env.BIZ_DB_PORT ?? 5432), + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + pool: { min: 1, max: 10 }, + active: true, +}; +``` + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; +import { BusinessDb } from './src/datasources/business.datasource.js'; + +export default defineStack({ + manifest: { id: 'app.example.crm-extend', namespace: 'biz', version: '1.0.0' }, + datasources: [BusinessDb], + objects: Object.values(objects), +}); +``` + +> **No** existe un helper `defineDatasource()`. Un datasource es simplemente +> un objeto `Datasource` que colocas en el array `datasources` — exactamente +> como hace el stack `examples/app-crm` en el repositorio del framework. + +## Vincular objetos a un datasource + +Cada objeto tiene un campo `datasource`. Su valor predeterminado es +`'default'` (la base de datos primaria). Establécelo para enrutar un objeto +específico hacia uno de tus sistemas conectados: + +```ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Customer = ObjectSchema.create({ + name: 'biz_customer', + label: 'Customer', + datasource: 'business_primary', // ← las lecturas/escrituras van a la BD de negocio + fields: { + name: Field.text({ label: 'Name', required: true }), + email: Field.text({ label: 'Email' }), + tier: Field.select({ label: 'Tier', options: [/* … */] }), + }, +}); +``` + +### Enrutamiento centralizado con `datasourceMapping` + +Vincular objetos uno por uno está bien para unos pocos. Para espacios de +nombres o paquetes enteros, declara las reglas de enrutamiento una sola vez +en el stack. Las reglas se evalúan en orden (o por `priority`); gana la +primera coincidencia: + +```ts +export default defineStack({ + datasources: [BusinessDb, AnalyticsReplica], + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { objectPattern: 'report_*', datasource: 'analytics_replica' }, + { package: 'com.example.logs', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ], +}); +``` + +Una regla coincide por `namespace`, `package`, `objectPattern` (glob) o +`default`, y nombra el `datasource` de destino. Esto mantiene las decisiones +de residencia de datos en un solo lugar en vez de dispersas por los archivos +de objetos. + +## Generar objetos a partir de tablas existentes + +No tienes que escribir a mano un objeto por cada tabla. La forma más rápida +de incorporar hoy un esquema heredado a ObjectOS es **usar un agente de +codificación (Claude Code) para escanear las tablas de negocio y generar +definiciones de objetos a nivel de código fuente** — un archivo +`*.object.ts` por tabla, exactamente con la forma que el framework espera. + +La aplicación de referencia [`hotcrm`](https://github.com/objectstack-ai/hotcrm) +es el ejemplo canónico de esa forma: cada tabla es un archivo +`src/objects/.object.ts` que usa `ObjectSchema.create({ … })` con +definiciones `Field.*`, todo ensamblado por `defineStack`. Un flujo típico: + +1. **Conecta** la base de datos de negocio como un datasource (arriba). +2. **Apunta Claude Code al esquema.** Pídele que introspeccione la base de + datos conectada — nombres de tablas, columnas, tipos, claves foráneas — y + genere un archivo `ObjectSchema.create` por tabla, mapeando las columnas + SQL a tipos `Field.*` y las claves foráneas a `Field.lookup(...)`. + Establece el `datasource` de cada objeto a tu conexión (o apóyate en + `datasourceMapping`). +3. **Revisa y refina** los objetos generados — añade labels, grupos de + campos, validaciones y permisos. La salida es código fuente ordinario que + te pertenece y confirmas, igual que `hotcrm/src/objects/*.object.ts`. +4. **Ejecuta.** Los objetos ahora leen y escriben en tus tablas existentes a + través del datasource vinculado. + +Como los objetos generados son código fuente sencillo, obtienes control +total: conserva lo que encaja, descarta las columnas que no quieras exponer y +superpón funciones de ObjectOS (seguimiento de historial, actividades, +reglas de compartición) sobre una base de datos que la plataforma nunca tuvo +que poseer. + +## Delegación de consultas según capacidades + +Cada datasource anuncia `DatasourceCapabilities` — si puede manejar filtros, +ordenación, paginación, agregaciones, joins, búsqueda de texto completo, +transacciones y más. ObjectQL usa esto para decidir qué **delegar (push +down)** a la base de datos frente a qué evaluar en memoria: + +| Capacidad | Efecto cuando se admite | +|---|---| +| `queryFilters` | Las cláusulas `WHERE` se ejecutan en la base de datos | +| `querySorting` / `queryPagination` | `ORDER BY` / `LIMIT` se ejecutan en el servidor | +| `queryAggregations` | `GROUP BY` / agregados se ejecutan en el servidor | +| `joins` | Los joins de objetos relacionados se ejecutan en el servidor | +| `fullTextSearch` | La búsqueda usa un índice nativo | +| `readOnly` | La conexión rechaza las escrituras | + +Un datasource SQL capaz delega casi todo; una fuente limitada o de solo +lectura obtiene los mismos resultados de consulta, solo que con más trabajo +hecho en el motor. Puedes sobrescribir el conjunto anunciado por datasource +mediante el campo `capabilities` cuando sepas más que el valor +predeterminado del driver. + +## IA sobre datos conectados + +Una vez que las tablas se modelan como objetos, **la capa de IA funciona +sobre ellas sin esfuerzo adicional**. Los agents y herramientas de ObjectOS +— `list_objects`, `describe_object`, `query_records`, `aggregate_data` y el +agente de chat de datos — pasan todos por ObjectQL, que enruta cada objeto a +su datasource vinculado. Eso significa: + +- Un usuario puede **hacer preguntas en lenguaje natural** sobre datos que + residen en el sistema de negocio heredado, y la respuesta se calcula contra + las filas reales. +- Las llamadas a herramientas y las consultas respetan los **permisos del + usuario que las realiza** — la IA nunca ve más de lo que el usuario que ha + iniciado sesión tiene permitido. +- Los mismos agents, flows y dashboards funcionan tanto si un objeto está + respaldado por la base de datos primaria como por un sistema de negocio + externo. + +Consulta [AI Service](/docs/configure/ai) y +[AI Agents](/docs/build/agents) para saber cómo conectar las capas de chat y +de agents, y [Runtime](/docs/configure/runtime) para la configuración de la +base de datos primaria que respalda el datasource `default`. + +## Notas de seguridad + +- **Nunca incluyas credenciales en línea.** Obtén host/usuario/contraseña de + variables de entorno (o de un gestor de secretos) como se muestra arriba. +- **Usa conexiones de solo lectura** para sistemas en los que no pretendes + escribir — establece la capacidad `readOnly` de la fuente (o un usuario de + BD de solo lectura) para que una escritura accidental no pueda llegar a + producción. +- **Limita el alcance con permisos.** Los permisos a nivel de objeto y de + campo se aplican a los datos conectados exactamente igual que a los objetos + nativos. + +## Hoja de ruta: federación de datasources externos en el producto + +El flujo anterior — conectar una base de datos, modelar objetos, generarlos +con un agente de codificación — funciona hoy con los bloques de construcción +incluidos. Una experiencia de **federación llave en mano** más rica está en +diseño activo bajo +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(estado: *Propuesto*). Previsto, pero **aún no disponible**: + +- Un `schemaMode` (`managed` / `external` / `validate-only`) para que + ObjectOS pueda vincularse a tablas que **no** posee sin intentar + migrarlas. +- Un subregistro de vinculación `external` en los objetos que mapea los + campos del objeto a columnas existentes. +- Una CLI `os datasource introspect` / `validate` para importar un esquema y + generar objetos en un solo paso. +- **Barreras de seguridad** en el arranque y en la escritura para esquemas de + propiedad externa, además de un asistente de Studio para todo el flujo. + +Hasta que eso llegue, prefiere la vía documentada: declara el datasource, +vincula objetos (generados o escritos a mano) y verifica primero contra una +copia que no sea de producción. + +## Adónde ir después + +- [Runtime](/docs/configure/runtime) — la base de datos primaria detrás de `default` +- [AI Service](/docs/configure/ai) — chat, embeddings, RAG, MCP +- [AI Agents](/docs/build/agents) — agents declarativos sobre tus objetos +- [Objects](/docs/build/objects) — la superficie de autoría `ObjectSchema.create` +- [datasources de `examples/app-crm`](https://github.com/objectstack-ai/framework/tree/main/examples/app-crm/src/datasources) — un ejemplo real de datasource + enrutamiento diff --git a/content/docs/configure/data-sources.fr.mdx b/content/docs/configure/data-sources.fr.mdx new file mode 100644 index 0000000..beccca7 --- /dev/null +++ b/content/docs/configure/data-sources.fr.mdx @@ -0,0 +1,271 @@ +--- +title: Sources de données +description: Connectez ObjectOS à vos bases de données métier existantes, routez-y les objets, et laissez l'IA interroger les données — nativement. +--- + +# Sources de données + +Une **datasource** est une connexion nommée vers un magasin de données +externe. En déclarant des datasources, vous pointez ObjectOS vers les +bases de données sur lesquelles votre activité repose déjà — un +PostgreSQL de production, un réplica MySQL de reporting, un cluster +MongoDB — puis vous y liez des objets. Une fois qu'un objet est lié, +tout le reste de la plateforme (l'API REST/GraphQL, les permissions, +les flux, les tableaux de bord et les **agents IA**) fonctionne sur ces +données de manière uniforme, sans se soucier de l'endroit où les lignes +résident physiquement. + +C'est l'un des chemins d'adoption les plus concrets d'ObjectOS : au lieu +de migrer un système hérité, vous vous y connectez, vous modélisez les +tables qui vous intéressent en tant qu'objets, et vous **l'enrichissez de +capacités natives IA** — chat, analyse, automatisation — par-dessus des +données qui restent exactement là où elles sont. + +## Ce qu'est une datasource + +Chaque datasource est un simple objet validé par `DatasourceSchema`. Les +champs principaux : + +| Champ | Rôle | +|---|---| +| `name` | Identifiant unique (`^[a-z_][a-z0-9_]*$`) référencé par les objets | +| `label` | Nom d'affichage lisible par un humain | +| `driver` | Quel driver gère la connexion (`postgres`, `mysql`, `sqlite`, `mongodb`, `memory`, ou un driver fourni par un plugin) | +| `config` | Paramètres de connexion spécifiques au driver (hôte, base de données, identifiants, …) | +| `pool` | Dimensionnement du pool de connexions (`min`, `max`, délais d'expiration) | +| `readReplicas` | Configurations de réplicas en lecture seule (optionnel) | +| `capabilities` | Surcharge ce que le driver annonce pouvoir pousser vers la base | +| `healthCheck` | Intervalle/délai d'expiration de la sonde de vivacité | +| `active` | Si la connexion est activée | + +Les drivers livrés sont : + +| Package de driver | `driver` | Backends | +|---|---|---| +| `@objectstack/driver-sql` | `postgres`, `mysql`, `sqlite` | PostgreSQL, MySQL, SQLite (via knex) | +| `@objectstack/driver-mongodb` | `mongodb` | MongoDB | +| `@objectstack/driver-memory` | `memory` | In-process (tests, démos) | +| `@objectstack/driver-sqlite-wasm` | `sqlite` (WASM) | SQLite dans WebContainers / navigateur | + +## Déclarer des datasources + +Les datasources sont déclarées sur le stack et assemblées avec +`defineStack`. Définissez chaque connexion comme un objet `Datasource` +typé, puis listez-les sous `datasources` : + +```ts +// src/datasources/business.datasource.ts +import type { Datasource } from '@objectstack/spec'; + +// Se connecter à une base de données de production EXISTANTE. Les +// identifiants proviennent de l'environnement — ne jamais inscrire de +// secrets en dur dans le code source. +export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + port: Number(process.env.BIZ_DB_PORT ?? 5432), + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + pool: { min: 1, max: 10 }, + active: true, +}; +``` + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; +import { BusinessDb } from './src/datasources/business.datasource.js'; + +export default defineStack({ + manifest: { id: 'app.example.crm-extend', namespace: 'biz', version: '1.0.0' }, + datasources: [BusinessDb], + objects: Object.values(objects), +}); +``` + +> Il n'y a **aucun** helper `defineDatasource()`. Une datasource est +> simplement un objet `Datasource` que vous placez dans le tableau +> `datasources` — exactement comme le fait le stack `examples/app-crm` +> dans le dépôt du framework. + +## Lier des objets à une datasource + +Chaque objet possède un champ `datasource`. Sa valeur par défaut est +`'default'` (la base de données primaire). Définissez-le pour router un +objet spécifique vers l'un de vos systèmes connectés : + +```ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Customer = ObjectSchema.create({ + name: 'biz_customer', + label: 'Customer', + datasource: 'business_primary', // ← lectures/écritures vers la base métier + fields: { + name: Field.text({ label: 'Name', required: true }), + email: Field.text({ label: 'Email' }), + tier: Field.select({ label: 'Tier', options: [/* … */] }), + }, +}); +``` + +### Routage centralisé avec `datasourceMapping` + +Lier les objets un par un convient pour une poignée d'entre eux. Pour des +espaces de noms ou des packages entiers, déclarez les règles de routage +une seule fois sur le stack. Les règles sont évaluées dans l'ordre (ou par +`priority`) ; la première correspondance l'emporte : + +```ts +export default defineStack({ + datasources: [BusinessDb, AnalyticsReplica], + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { objectPattern: 'report_*', datasource: 'analytics_replica' }, + { package: 'com.example.logs', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ], +}); +``` + +Une règle correspond par `namespace`, `package`, `objectPattern` (glob) +ou `default`, et nomme la `datasource` cible. Cela maintient les décisions +de résidence des données en un seul endroit au lieu de les disperser dans +les fichiers d'objets. + +## Générer des objets à partir de tables existantes + +Vous n'avez pas à écrire à la main un objet pour chaque table. La manière +la plus rapide aujourd'hui d'intégrer un schéma hérité dans ObjectOS est +d'**utiliser un agent de codage (Claude Code) pour scanner les tables +métier et générer des définitions d'objets au niveau du code source** — +un fichier `*.object.ts` par table, exactement dans la forme attendue par +le framework. + +L'application de référence [`hotcrm`](https://github.com/objectstack-ai/hotcrm) +est l'exemple canonique de cette forme : chaque table est un fichier +`src/objects/.object.ts` utilisant `ObjectSchema.create({ … })` +avec des définitions `Field.*`, le tout assemblé par `defineStack`. Un +flux typique : + +1. **Connectez** la base de données métier en tant que datasource + (ci-dessus). +2. **Pointez Claude Code vers le schéma.** Demandez-lui d'introspecter la + base de données connectée — noms de tables, colonnes, types, clés + étrangères — et de générer un fichier `ObjectSchema.create` par table, + en mappant les colonnes SQL vers des types `Field.*` et les clés + étrangères vers `Field.lookup(...)`. Définissez la `datasource` de + chaque objet sur votre connexion (ou reposez-vous sur + `datasourceMapping`). +3. **Révisez et affinez** les objets générés — ajoutez des libellés, des + groupes de champs, des validations et des permissions. La sortie est du + code source ordinaire que vous possédez et committez, tout comme + `hotcrm/src/objects/*.object.ts`. +4. **Exécutez.** Les objets lisent et écrivent désormais dans vos tables + existantes via la datasource liée. + +Comme les objets générés sont du simple code source, vous gardez un +contrôle total : conservez ce qui convient, supprimez les colonnes que +vous ne souhaitez pas exposer, et superposez les fonctionnalités ObjectOS +(suivi de l'historique, activités, règles de partage) par-dessus une base +de données que la plateforme n'a jamais eu à posséder. + +## Pushdown des requêtes selon les capacités + +Chaque datasource annonce ses `DatasourceCapabilities` — si elle peut +gérer les filtres, le tri, la pagination, les agrégations, les jointures, +la recherche plein texte, les transactions, et plus encore. ObjectQL +utilise cela pour décider ce qu'il **pousse** vers la base de données par +rapport à ce qu'il évalue en mémoire : + +| Capacité | Effet lorsqu'elle est prise en charge | +|---|---| +| `queryFilters` | Les clauses `WHERE` s'exécutent dans la base de données | +| `querySorting` / `queryPagination` | `ORDER BY` / `LIMIT` s'exécutent côté serveur | +| `queryAggregations` | `GROUP BY` / agrégats s'exécutent côté serveur | +| `joins` | Les jointures entre objets liés s'exécutent côté serveur | +| `fullTextSearch` | La recherche utilise un index natif | +| `readOnly` | La connexion rejette les écritures | + +Une datasource SQL capable pousse presque tout vers la base ; une source +limitée ou en lecture seule obtient les mêmes résultats de requête, avec +simplement davantage de travail effectué dans le moteur. Vous pouvez +surcharger l'ensemble annoncé par datasource via le champ `capabilities` +lorsque vous en savez plus que la valeur par défaut du driver. + +## L'IA sur des données connectées + +Une fois les tables modélisées en tant qu'objets, **la couche IA +fonctionne dessus gratuitement**. Les agents et outils d'ObjectOS — +`list_objects`, `describe_object`, `query_records`, `aggregate_data` et +l'agent de chat sur les données — passent tous par ObjectQL, qui route +chaque objet vers sa datasource liée. Cela signifie que : + +- Un utilisateur peut **poser des questions en langage naturel** sur des + données qui résident dans le système métier hérité, et la réponse est + calculée sur les vraies lignes. +- Les appels d'outils et les requêtes respectent les **permissions de + l'utilisateur appelant** — l'IA ne voit jamais plus que ce que + l'utilisateur connecté est autorisé à voir. +- Les mêmes agents, flux et tableaux de bord fonctionnent qu'un objet soit + adossé à la base de données primaire ou à un système métier externe. + +Consultez [Service IA](/docs/configure/ai) et +[AI Agents](/docs/build/agents) pour savoir comment câbler les couches de +chat et d'agents, et [Runtime](/docs/configure/runtime) pour la +configuration de la base de données primaire qui sous-tend la datasource +`default`. + +## Notes de sécurité + +- **N'inscrivez jamais d'identifiants en dur.** Récupérez + l'hôte/l'utilisateur/le mot de passe depuis des variables + d'environnement (ou un gestionnaire de secrets) comme montré ci-dessus. +- **Utilisez des connexions en lecture seule** pour les systèmes dans + lesquels vous n'avez pas l'intention d'écrire — définissez la capacité + `readOnly` de la source (ou un utilisateur de base de données en lecture + seule) afin qu'une écriture accidentelle ne puisse pas atteindre la + production. +- **Limitez la portée avec les permissions.** Les permissions au niveau + des objets et des champs s'appliquent aux données connectées exactement + comme aux objets natifs. + +## Feuille de route : fédération de sources de données externes intégrée au produit + +Le flux ci-dessus — connecter une base de données, modéliser des objets, +les générer avec un agent de codage — fonctionne dès aujourd'hui avec les +briques livrées. Une expérience de **fédération clé en main** plus riche +est en cours de conception active sous +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(statut : *Proposed*). Prévu, **pas encore livré** : + +- Un `schemaMode` (`managed` / `external` / `validate-only`) afin + qu'ObjectOS puisse se lier à des tables qu'il ne **possède pas** sans + essayer de les migrer. +- Un sous-enregistrement de liaison `external` sur les objets, mappant les + champs d'objet vers des colonnes existantes. +- Une CLI `os datasource introspect` / `validate` pour importer un schéma + et générer le squelette des objets en une seule étape. +- Des **garde-fous de sécurité** au démarrage et à l'écriture pour les + schémas appartenant à des systèmes externes, plus un assistant Studio + pour l'ensemble du flux. + +En attendant que ceux-ci arrivent, préférez le chemin documenté : +déclarez la datasource, liez les objets (générés ou écrits à la main), et +vérifiez d'abord sur une copie hors production. + +## Pour aller plus loin + +- [Runtime](/docs/configure/runtime) — la base de données primaire derrière `default` +- [Service IA](/docs/configure/ai) — chat, embeddings, RAG, MCP +- [AI Agents](/docs/build/agents) — agents déclaratifs sur vos objets +- [Objects](/docs/build/objects) — la surface d'écriture `ObjectSchema.create` +- [Sources de données de `examples/app-crm`](https://github.com/objectstack-ai/framework/tree/main/examples/app-crm/src/datasources) — un exemple réel de datasource + routage diff --git a/content/docs/configure/data-sources.ja.mdx b/content/docs/configure/data-sources.ja.mdx new file mode 100644 index 0000000..5866fbc --- /dev/null +++ b/content/docs/configure/data-sources.ja.mdx @@ -0,0 +1,178 @@ +--- +title: データソース +description: ObjectOS を既存のビジネスデータベースに接続し、オブジェクトをそこへルーティングして、AI にそのデータをネイティブにクエリさせます。 +--- + +# データソース + +**データソース**とは、外部データストアへの名前付き接続です。データソースを宣言することで、ObjectOS をビジネスがすでに稼働しているデータベース — 本番の PostgreSQL、レポート用の MySQL レプリカ、MongoDB クラスター — に向け、そこへオブジェクトをバインドします。オブジェクトが一度バインドされると、プラットフォームの他のすべて(REST/GraphQL API、権限、フロー、ダッシュボード、そして **AI エージェント**)が、行が物理的にどこにあるかを気にすることなく、そのデータに対して一様に動作します。 + +これは ObjectOS の最も実践的な導入経路の 1 つです。レガシーシステムを移行する代わりに、それに接続し、関心のあるテーブルをオブジェクトとしてモデル化し、まさにあるべき場所にとどまるデータの上に **AI ネイティブな機能** — チャット、分析、自動化 — を**拡張**します。 + +## データソースとは + +各データソースは `DatasourceSchema` によって検証されるプレーンなオブジェクトです。コアとなるフィールド: + +| フィールド | 目的 | +|---|---| +| `name` | オブジェクトが参照する一意の識別子(`^[a-z_][a-z0-9_]*$`) | +| `label` | 人間が読める表示名 | +| `driver` | 接続を処理するドライバー(`postgres`、`mysql`、`sqlite`、`mongodb`、`memory`、またはプラグインが提供するドライバー) | +| `config` | ドライバー固有の接続設定(ホスト、データベース、認証情報、…) | +| `pool` | コネクションプールのサイジング(`min`、`max`、タイムアウト) | +| `readReplicas` | 任意の読み取り専用レプリカ設定 | +| `capabilities` | ドライバーがプッシュダウン可能と公表する内容を上書き | +| `healthCheck` | 死活監視プローブの間隔/タイムアウト | +| `active` | 接続を有効にするかどうか | + +同梱されているドライバーは以下のとおりです: + +| ドライバーパッケージ | `driver` | バックエンド | +|---|---|---| +| `@objectstack/driver-sql` | `postgres`、`mysql`、`sqlite` | PostgreSQL、MySQL、SQLite(knex 経由) | +| `@objectstack/driver-mongodb` | `mongodb` | MongoDB | +| `@objectstack/driver-memory` | `memory` | インプロセス(テスト、デモ) | +| `@objectstack/driver-sqlite-wasm` | `sqlite`(WASM) | WebContainers / ブラウザ上の SQLite | + +## データソースの宣言 + +データソースはスタック上で宣言され、`defineStack` で組み立てられます。各接続を型付きの `Datasource` オブジェクトとして定義し、それらを `datasources` の下に列挙します: + +```ts +// src/datasources/business.datasource.ts +import type { Datasource } from '@objectstack/spec'; + +// 既存の本番データベースに接続します。認証情報は環境から取得します — +// ソースにシークレットをインラインで書かないでください。 +export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + port: Number(process.env.BIZ_DB_PORT ?? 5432), + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + pool: { min: 1, max: 10 }, + active: true, +}; +``` + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; +import { BusinessDb } from './src/datasources/business.datasource.js'; + +export default defineStack({ + manifest: { id: 'app.example.crm-extend', namespace: 'biz', version: '1.0.0' }, + datasources: [BusinessDb], + objects: Object.values(objects), +}); +``` + +> `defineDatasource()` ヘルパーは**存在しません**。データソースとは、`datasources` 配列に配置する単なる `Datasource` オブジェクトです — フレームワークリポジトリの `examples/app-crm` スタックがまさにそうしているとおりです。 + +## オブジェクトをデータソースにバインドする + +すべてのオブジェクトは `datasource` フィールドを持ちます。デフォルトは `'default'`(プライマリデータベース)です。特定のオブジェクトを接続済みシステムの 1 つにルーティングするには、これを設定します: + +```ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Customer = ObjectSchema.create({ + name: 'biz_customer', + label: 'Customer', + datasource: 'business_primary', // ← 読み書きはビジネス DB へ行く + fields: { + name: Field.text({ label: 'Name', required: true }), + email: Field.text({ label: 'Email' }), + tier: Field.select({ label: 'Tier', options: [/* … */] }), + }, +}); +``` + +### `datasourceMapping` による集中ルーティング + +オブジェクトを 1 つずつバインドするのは少数なら問題ありません。名前空間やパッケージ全体については、ルーティングルールをスタック上で一度だけ宣言します。ルールは順番(または `priority`)に評価され、最初にマッチしたものが優先されます: + +```ts +export default defineStack({ + datasources: [BusinessDb, AnalyticsReplica], + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { objectPattern: 'report_*', datasource: 'analytics_replica' }, + { package: 'com.example.logs', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ], +}); +``` + +ルールは `namespace`、`package`、`objectPattern`(glob)、または `default` でマッチし、ターゲットの `datasource` を指定します。これにより、データレジデンシーの判断がオブジェクトファイル全体に散らばるのではなく、1 か所にまとまります。 + +## 既存テーブルからのオブジェクト生成 + +すべてのテーブルについて手作業でオブジェクトを書く必要はありません。今日レガシースキーマを ObjectOS に取り込む最速の方法は、**コーディングエージェント(Claude Code)を使ってビジネステーブルをスキャンし、ソースレベルのオブジェクト定義を生成する**ことです — テーブルごとに 1 つの `*.object.ts` ファイルを、フレームワークが期待するまさにその形で。 + +[`hotcrm`](https://github.com/objectstack-ai/hotcrm) リファレンスアプリは、その形の標準的な例です。各テーブルは `Field.*` 定義を持つ `ObjectSchema.create({ … })` を使った `src/objects/.object.ts` ファイルであり、すべてが `defineStack` によって組み立てられます。典型的なフロー: + +1. ビジネスデータベースをデータソースとして**接続**します(上記)。 +2. **Claude Code をスキーマに向けます。** 接続されたデータベース — テーブル名、カラム、型、外部キー — をイントロスペクトし、テーブルごとに 1 つの `ObjectSchema.create` ファイルを生成し、SQL カラムを `Field.*` 型へ、外部キーを `Field.lookup(...)` へマッピングするよう依頼します。各オブジェクトの `datasource` を接続先に設定するか、`datasourceMapping` に頼ります。 +3. 生成されたオブジェクトを**レビューして精緻化**します — ラベル、フィールドグループ、バリデーション、権限を追加します。出力は、`hotcrm/src/objects/*.object.ts` とまったく同じように、あなたが所有しコミットする通常のソースです。 +4. **実行します。** オブジェクトは、バインドされたデータソースを通じて、既存のテーブルを読み書きするようになります。 + +生成されたオブジェクトはプレーンなソースなので、完全な制御が得られます。フィットするものは残し、公開したくないカラムは捨て、プラットフォームが一度も所有する必要のなかったデータベースの上に ObjectOS の機能(履歴追跡、アクティビティ、共有ルール)を重ねられます。 + +## ケーパビリティを考慮したクエリプッシュダウン + +各データソースは `DatasourceCapabilities` を公表します — フィルター、ソート、ページネーション、集計、結合、全文検索、トランザクションなどを処理できるかどうかです。ObjectQL はこれを使って、何をデータベースに**プッシュダウン**し、何をメモリ内で評価するかを決定します: + +| ケーパビリティ | サポート時の効果 | +|---|---| +| `queryFilters` | `WHERE` 句がデータベースで実行される | +| `querySorting` / `queryPagination` | `ORDER BY` / `LIMIT` がサーバー側で実行される | +| `queryAggregations` | `GROUP BY` / 集計がサーバー側で実行される | +| `joins` | 関連オブジェクトの結合がサーバー側で実行される | +| `fullTextSearch` | 検索がネイティブインデックスにヒットする | +| `readOnly` | 接続が書き込みを拒否する | + +高機能な SQL データソースはほぼすべてをプッシュダウンします。制限のある、または読み取り専用のソースでも同じクエリ結果が得られますが、エンジン内でより多くの処理が行われるだけです。ドライバーのデフォルトより自分の方が把握している場合は、`capabilities` フィールドを使ってデータソースごとに公表されるセットを上書きできます。 + +## 接続データに対する AI + +テーブルがオブジェクトとしてモデル化されると、**AI レイヤーはそれらに対して無償で動作します**。ObjectOS のエージェントとツール — `list_objects`、`describe_object`、`query_records`、`aggregate_data`、そしてデータチャットエージェント — はすべて ObjectQL を経由し、各オブジェクトをバインドされたデータソースへルーティングします。これは次のことを意味します: + +- ユーザーはレガシービジネスシステムにあるデータについて**自然言語で質問**でき、その答えは実際の行に対して計算されます。 +- ツール呼び出しとクエリは**呼び出しユーザーの権限**を尊重します — AI はサインインしているユーザーに許可された以上のものを決して見ません。 +- 同じエージェント、フロー、ダッシュボードは、オブジェクトがプライマリデータベースに支えられていても外部ビジネスシステムに支えられていても動作します。 + +チャットとエージェントのレイヤーを接続する方法については [AI サービス](/docs/configure/ai) と [AI Agents](/docs/build/agents) を、`default` データソースを支えるプライマリデータベースの設定については [Runtime](/docs/configure/runtime) を参照してください。 + +## セキュリティに関する注意 + +- **認証情報をインラインで書かないでください。** 上記のように、ホスト/ユーザー/パスワードを環境変数(またはシークレットマネージャー)から取得してください。 +- 書き込むつもりのないシステムには**読み取り専用接続を使用してください** — ソースの `readOnly` ケーパビリティ(または読み取り専用 DB ユーザー)を設定して、誤った書き込みが本番に到達できないようにします。 +- **権限でスコープを限定してください。** オブジェクトレベルおよびフィールドレベルの権限は、ネイティブオブジェクトとまったく同じように接続データにも適用されます。 + +## ロードマップ: 製品内の外部データソースフェデレーション + +上記のフロー — データベースを接続し、オブジェクトをモデル化し、コーディングエージェントで生成する — は、同梱のビルディングブロックで今日機能します。よりリッチな**ターンキー型フェデレーション**体験が [ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md)(ステータス: *Proposed*)の下で活発に設計中です。計画されている、**まだ出荷されていない**もの: + +- ObjectOS が、所有して**いない**テーブルを移行しようとせずにバインドできるようにする `schemaMode`(`managed` / `external` / `validate-only`)。 +- オブジェクトのフィールドを既存のカラムにマッピングする、オブジェクト上の `external` バインディングサブレコード。 +- スキーマをインポートして 1 ステップでオブジェクトをスキャフォールドする `os datasource introspect` / `validate` CLI。 +- 外部所有のスキーマに対する起動時および書き込み時の**安全ゲート**、加えてフロー全体のための Studio ウィザード。 + +それらが実現するまでは、ドキュメント化された経路を優先してください。データソースを宣言し、オブジェクト(生成またはハンドライト)をバインドし、まず非本番のコピーに対して検証します。 + +## 次に読むべきもの + +- [Runtime](/docs/configure/runtime) — `default` の背後にあるプライマリデータベース +- [AI サービス](/docs/configure/ai) — チャット、埋め込み、RAG、MCP +- [AI Agents](/docs/build/agents) — オブジェクトに対する宣言的なエージェント +- [Objects](/docs/build/objects) — `ObjectSchema.create` の作成サーフェス +- [`examples/app-crm` datasources](https://github.com/objectstack-ai/framework/tree/main/examples/app-crm/src/datasources) — 実際のデータソース + ルーティングの例 diff --git a/content/docs/configure/data-sources.ko.mdx b/content/docs/configure/data-sources.ko.mdx new file mode 100644 index 0000000..6fbd1af --- /dev/null +++ b/content/docs/configure/data-sources.ko.mdx @@ -0,0 +1,178 @@ +--- +title: 데이터 소스 +description: ObjectOS를 기존 비즈니스 데이터베이스에 연결하고, 객체를 해당 데이터베이스로 라우팅하며, AI가 데이터를 네이티브하게 쿼리하도록 하세요. +--- + +# 데이터 소스 + +**datasource**는 외부 데이터 저장소에 대한 이름이 지정된 연결입니다. 데이터 소스를 선언함으로써 ObjectOS를 비즈니스가 이미 운영 중인 데이터베이스 — 프로덕션 PostgreSQL, 리포팅용 MySQL 복제본, MongoDB 클러스터 — 로 지정한 다음, 객체를 거기에 바인딩합니다. 객체가 일단 바인딩되면 플랫폼의 나머지 모든 것(REST/GraphQL API, 권한, 플로우, 대시보드, 그리고 **AI 에이전트**)이 행이 물리적으로 어디에 있는지 신경 쓰지 않고 해당 데이터에 대해 균일하게 동작합니다. + +이것은 ObjectOS의 가장 실용적인 도입 경로 중 하나입니다: 레거시 시스템을 마이그레이션하는 대신, 거기에 연결하고, 관심 있는 테이블을 객체로 모델링한 다음, 데이터를 있는 그대로 두면서 그 위에 **AI 네이티브 기능** — 채팅, 분석, 자동화 — 으로 확장합니다. + +## 데이터 소스란 + +각 데이터 소스는 `DatasourceSchema`로 검증되는 평범한 객체입니다. 핵심 필드는 다음과 같습니다: + +| 필드 | 용도 | +|---|---| +| `name` | 객체가 참조하는 고유 식별자 (`^[a-z_][a-z0-9_]*$`) | +| `label` | 사람이 읽을 수 있는 표시 이름 | +| `driver` | 연결을 처리하는 드라이버 (`postgres`, `mysql`, `sqlite`, `mongodb`, `memory`, 또는 플러그인이 제공하는 드라이버) | +| `config` | 드라이버별 연결 설정 (host, database, 자격 증명, …) | +| `pool` | 연결 풀 크기 (`min`, `max`, 타임아웃) | +| `readReplicas` | 선택적 읽기 전용 복제본 구성 | +| `capabilities` | 드라이버가 푸시다운할 수 있다고 알리는 내용을 재정의 | +| `healthCheck` | 활성 상태 프로브 간격/타임아웃 | +| `active` | 연결 활성화 여부 | + +기본 제공되는 드라이버는 다음과 같습니다: + +| 드라이버 패키지 | `driver` | 백엔드 | +|---|---|---| +| `@objectstack/driver-sql` | `postgres`, `mysql`, `sqlite` | PostgreSQL, MySQL, SQLite (knex 경유) | +| `@objectstack/driver-mongodb` | `mongodb` | MongoDB | +| `@objectstack/driver-memory` | `memory` | 인프로세스 (테스트, 데모) | +| `@objectstack/driver-sqlite-wasm` | `sqlite` (WASM) | WebContainers / 브라우저의 SQLite | + +## 데이터 소스 선언하기 + +데이터 소스는 스택에 선언되며 `defineStack`으로 조립됩니다. 각 연결을 타입이 지정된 `Datasource` 객체로 정의한 다음, `datasources` 아래에 나열하세요: + +```ts +// src/datasources/business.datasource.ts +import type { Datasource } from '@objectstack/spec'; + +// 기존 프로덕션 데이터베이스에 연결합니다. 자격 증명은 환경에서 +// 가져옵니다 — 소스에 비밀 값을 인라인으로 넣지 마세요. +export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + port: Number(process.env.BIZ_DB_PORT ?? 5432), + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + pool: { min: 1, max: 10 }, + active: true, +}; +``` + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; +import { BusinessDb } from './src/datasources/business.datasource.js'; + +export default defineStack({ + manifest: { id: 'app.example.crm-extend', namespace: 'biz', version: '1.0.0' }, + datasources: [BusinessDb], + objects: Object.values(objects), +}); +``` + +> `defineDatasource()` 헬퍼는 **없습니다**. 데이터 소스는 `datasources` 배열에 넣는 `Datasource` 객체일 뿐입니다 — 프레임워크 저장소의 `examples/app-crm` 스택이 하는 것과 정확히 동일합니다. + +## 객체를 데이터 소스에 바인딩하기 + +모든 객체에는 `datasource` 필드가 있습니다. 기본값은 `'default'`(기본 데이터베이스)입니다. 특정 객체를 연결된 시스템 중 하나로 라우팅하려면 이 값을 설정하세요: + +```ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Customer = ObjectSchema.create({ + name: 'biz_customer', + label: 'Customer', + datasource: 'business_primary', // ← 읽기/쓰기가 비즈니스 DB로 전달됨 + fields: { + name: Field.text({ label: 'Name', required: true }), + email: Field.text({ label: 'Email' }), + tier: Field.select({ label: 'Tier', options: [/* … */] }), + }, +}); +``` + +### `datasourceMapping`을 사용한 중앙 집중식 라우팅 + +객체를 하나씩 바인딩하는 것은 소수일 때는 괜찮습니다. 네임스페이스나 패키지 전체에 대해서는 스택에 라우팅 규칙을 한 번 선언하세요. 규칙은 순서대로(또는 `priority`에 따라) 평가되며, 첫 번째 일치가 우선합니다: + +```ts +export default defineStack({ + datasources: [BusinessDb, AnalyticsReplica], + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { objectPattern: 'report_*', datasource: 'analytics_replica' }, + { package: 'com.example.logs', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ], +}); +``` + +규칙은 `namespace`, `package`, `objectPattern`(glob), 또는 `default`로 일치하며, 대상 `datasource`를 지정합니다. 이렇게 하면 데이터 거주(data-residency) 결정이 객체 파일 전반에 흩어지는 대신 한곳에 모입니다. + +## 기존 테이블에서 객체 생성하기 + +모든 테이블에 대해 객체를 손으로 작성할 필요는 없습니다. 오늘날 레거시 스키마를 ObjectOS로 가져오는 가장 빠른 방법은 **코딩 에이전트(Claude Code)를 사용하여 비즈니스 테이블을 스캔하고 소스 수준의 객체 정의를 생성**하는 것입니다 — 테이블당 하나의 `*.object.ts` 파일로, 프레임워크가 기대하는 형태 그대로입니다. + +[`hotcrm`](https://github.com/objectstack-ai/hotcrm) 레퍼런스 앱이 그 형태의 표준 예시입니다: 각 테이블은 `Field.*` 정의와 함께 `ObjectSchema.create({ … })`를 사용하는 `src/objects/.object.ts` 파일이며, 모두 `defineStack`으로 조립됩니다. 일반적인 흐름은 다음과 같습니다: + +1. 비즈니스 데이터베이스를 데이터 소스로 **연결**합니다(위 참조). +2. **Claude Code를 스키마로 지정합니다.** 연결된 데이터베이스 — 테이블 이름, 컬럼, 타입, 외래 키 — 를 인트로스펙션하고, SQL 컬럼을 `Field.*` 타입에 매핑하고 외래 키를 `Field.lookup(...)`에 매핑하여 테이블당 하나의 `ObjectSchema.create` 파일을 생성하도록 요청하세요. 각 객체의 `datasource`를 연결로 설정하거나(또는 `datasourceMapping`에 의존하세요). +3. 생성된 객체를 **검토하고 다듬습니다** — 레이블, 필드 그룹, 검증, 권한을 추가하세요. 출력은 `hotcrm/src/objects/*.object.ts`와 마찬가지로 여러분이 소유하고 커밋하는 일반적인 소스입니다. +4. **실행합니다.** 이제 객체는 바인딩된 데이터 소스를 통해 기존 테이블을 읽고 씁니다. + +생성된 객체가 평범한 소스이기 때문에 완전한 제어권을 갖습니다: 맞는 것은 유지하고, 노출하고 싶지 않은 컬럼은 버리고, 플랫폼이 소유할 필요가 전혀 없었던 데이터베이스 위에 ObjectOS 기능(이력 추적, 활동, 공유 규칙)을 계층화하세요. + +## 기능 인식 쿼리 푸시다운 + +각 데이터 소스는 `DatasourceCapabilities`를 알립니다 — 필터, 정렬, 페이지네이션, 집계, 조인, 전문(full-text) 검색, 트랜잭션 등을 처리할 수 있는지 여부입니다. ObjectQL은 이를 사용하여 데이터베이스에 **푸시다운**할 것과 메모리에서 평가할 것을 결정합니다: + +| 기능 | 지원될 때의 효과 | +|---|---| +| `queryFilters` | `WHERE` 절이 데이터베이스에서 실행됨 | +| `querySorting` / `queryPagination` | `ORDER BY` / `LIMIT`이 서버 측에서 실행됨 | +| `queryAggregations` | `GROUP BY` / 집계가 서버 측에서 실행됨 | +| `joins` | 관련 객체 조인이 서버 측에서 실행됨 | +| `fullTextSearch` | 검색이 네이티브 인덱스를 사용함 | +| `readOnly` | 연결이 쓰기를 거부함 | + +기능이 풍부한 SQL 데이터 소스는 거의 모든 것을 푸시다운합니다. 제한적이거나 읽기 전용인 소스는 동일한 쿼리 결과를 얻지만, 더 많은 작업이 엔진에서 수행됩니다. 드라이버 기본값보다 더 잘 알고 있을 때는 `capabilities` 필드를 통해 데이터 소스별로 알려진 기능 집합을 재정의할 수 있습니다. + +## 연결된 데이터에 대한 AI + +테이블이 일단 객체로 모델링되면 **AI 계층이 무료로 그 위에서 동작합니다**. ObjectOS의 에이전트와 도구 — `list_objects`, `describe_object`, `query_records`, `aggregate_data`, 그리고 데이터 채팅 에이전트 — 는 모두 ObjectQL을 거치며, ObjectQL은 각 객체를 바인딩된 데이터 소스로 라우팅합니다. 이것이 의미하는 바는: + +- 사용자가 레거시 비즈니스 시스템에 있는 데이터에 대해 **자연어로 질문할 수 있고**, 답은 실제 행에 대해 계산됩니다. +- 도구 호출과 쿼리는 **호출하는 사용자의 권한**을 존중합니다 — AI는 로그인한 사용자에게 허용된 것보다 더 많이 보지 못합니다. +- 동일한 에이전트, 플로우, 대시보드가 객체가 기본 데이터베이스에 의해 뒷받침되든 외부 비즈니스 시스템에 의해 뒷받침되든 동작합니다. + +채팅 및 에이전트 계층을 연결하는 방법은 [AI 서비스](/docs/configure/ai)와 [AI Agents](/docs/build/agents)를 참고하고, `default` 데이터 소스를 뒷받침하는 기본 데이터베이스 구성은 [Runtime](/docs/configure/runtime)을 참고하세요. + +## 보안 참고 사항 + +- **자격 증명을 인라인으로 넣지 마세요.** 위에서 보여준 것처럼 host/user/password를 환경 변수(또는 시크릿 매니저)에서 가져오세요. +- 쓸 의도가 없는 시스템에는 **읽기 전용 연결을 사용하세요** — 소스의 `readOnly` 기능(또는 읽기 전용 DB 사용자)을 설정하여 우발적인 쓰기가 프로덕션에 도달하지 못하게 하세요. +- **권한으로 범위를 지정하세요.** 객체 및 필드 수준 권한은 네이티브 객체에 적용되는 것과 정확히 동일하게 연결된 데이터에 적용됩니다. + +## 로드맵: 제품 내 외부 데이터 소스 페더레이션 + +위의 흐름 — 데이터베이스 연결, 객체 모델링, 코딩 에이전트로 생성 — 은 기본 제공 빌딩 블록으로 오늘날 동작합니다. 더 풍부한 **턴키(turn-key) 페더레이션** 경험이 [ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md)(상태: *Proposed*) 아래에서 활발히 설계되고 있습니다. 계획되어 있지만 **아직 출시되지 않은** 것들: + +- ObjectOS가 소유하지 **않은** 테이블을 마이그레이션하려 시도하지 않고 바인딩할 수 있도록 하는 `schemaMode`(`managed` / `external` / `validate-only`). +- 객체 필드를 기존 컬럼에 매핑하는 객체의 `external` 바인딩 하위 레코드. +- 한 단계로 스키마를 가져오고 객체를 스캐폴딩하는 `os datasource introspect` / `validate` CLI. +- 외부 소유 스키마에 대한 부팅 시점 및 쓰기 시점 **안전 게이트**, 그리고 전체 흐름을 위한 Studio 마법사. + +이것들이 출시되기 전까지는 문서화된 경로를 선호하세요: 데이터 소스를 선언하고, 객체를 바인딩하고(생성된 것이든 손으로 작성한 것이든), 먼저 비프로덕션 사본에 대해 검증하세요. + +## 다음으로 갈 곳 + +- [Runtime](/docs/configure/runtime) — `default` 뒤에 있는 기본 데이터베이스 +- [AI 서비스](/docs/configure/ai) — 채팅, 임베딩, RAG, MCP +- [AI Agents](/docs/build/agents) — 객체에 대한 선언적 에이전트 +- [Objects](/docs/build/objects) — `ObjectSchema.create` 작성 인터페이스 +- [`examples/app-crm` 데이터 소스](https://github.com/objectstack-ai/framework/tree/main/examples/app-crm/src/datasources) — 실제 데이터 소스 + 라우팅 예제 diff --git a/content/docs/configure/data-sources.mdx b/content/docs/configure/data-sources.mdx new file mode 100644 index 0000000..e671a1c --- /dev/null +++ b/content/docs/configure/data-sources.mdx @@ -0,0 +1,248 @@ +--- +title: Data Sources +description: Connect ObjectOS to your existing business databases, route objects to them, and let AI query the data — natively. +--- + +# Data Sources + +A **datasource** is a named connection to an external data store. By +declaring datasources, you point ObjectOS at the databases your business +already runs on — a production PostgreSQL, a reporting MySQL replica, a +MongoDB cluster — and then bind objects to them. Once an object is bound, +everything else in the platform (the REST/GraphQL API, permissions, +flows, dashboards, and **AI agents**) works against that data uniformly, +without caring where the rows physically live. + +This is one of ObjectOS's most practical adoption paths: instead of +migrating a legacy system, you connect to it, model the tables you care +about as objects, and **extend it with AI-native capabilities** — chat, +analysis, automation — on top of data that stays exactly where it is. + +## What a datasource is + +Each datasource is a plain object validated by `DatasourceSchema`. The +core fields: + +| Field | Purpose | +|---|---| +| `name` | Unique identifier (`^[a-z_][a-z0-9_]*$`) objects reference | +| `label` | Human-readable display name | +| `driver` | Which driver handles the connection (`postgres`, `mysql`, `sqlite`, `mongodb`, `memory`, or a plugin-contributed driver) | +| `config` | Driver-specific connection settings (host, database, credentials, …) | +| `pool` | Connection-pool sizing (`min`, `max`, timeouts) | +| `readReplicas` | Optional read-only replica configs | +| `capabilities` | Override what the driver advertises it can push down | +| `healthCheck` | Liveness probe interval/timeout | +| `active` | Whether the connection is enabled | + +The shipped drivers are: + +| Driver package | `driver` | Backends | +|---|---|---| +| `@objectstack/driver-sql` | `postgres`, `mysql`, `sqlite` | PostgreSQL, MySQL, SQLite (via knex) | +| `@objectstack/driver-mongodb` | `mongodb` | MongoDB | +| `@objectstack/driver-memory` | `memory` | In-process (tests, demos) | +| `@objectstack/driver-sqlite-wasm` | `sqlite` (WASM) | SQLite in WebContainers / browser | + +## Declaring datasources + +Datasources are declared on the stack and assembled with `defineStack`. +Define each connection as a typed `Datasource` object, then list them +under `datasources`: + +```ts +// src/datasources/business.datasource.ts +import type { Datasource } from '@objectstack/spec'; + +// Connect to an EXISTING production database. Credentials come from the +// environment — never inline secrets in source. +export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + port: Number(process.env.BIZ_DB_PORT ?? 5432), + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + pool: { min: 1, max: 10 }, + active: true, +}; +``` + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; +import { BusinessDb } from './src/datasources/business.datasource.js'; + +export default defineStack({ + manifest: { id: 'app.example.crm-extend', namespace: 'biz', version: '1.0.0' }, + datasources: [BusinessDb], + objects: Object.values(objects), +}); +``` + +> There is **no** `defineDatasource()` helper. A datasource is just a +> `Datasource` object you place in the `datasources` array — exactly like +> the `examples/app-crm` stack does in the framework repo. + +## Binding objects to a datasource + +Every object has a `datasource` field. It defaults to `'default'` (the +primary database). Set it to route a specific object at one of your +connected systems: + +```ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Customer = ObjectSchema.create({ + name: 'biz_customer', + label: 'Customer', + datasource: 'business_primary', // ← reads/writes go to the business DB + fields: { + name: Field.text({ label: 'Name', required: true }), + email: Field.text({ label: 'Email' }), + tier: Field.select({ label: 'Tier', options: [/* … */] }), + }, +}); +``` + +### Centralized routing with `datasourceMapping` + +Binding objects one by one is fine for a handful. For whole namespaces or +packages, declare routing rules once on the stack. Rules are evaluated in +order (or by `priority`); first match wins: + +```ts +export default defineStack({ + datasources: [BusinessDb, AnalyticsReplica], + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { objectPattern: 'report_*', datasource: 'analytics_replica' }, + { package: 'com.example.logs', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ], +}); +``` + +A rule matches by `namespace`, `package`, `objectPattern` (glob), or +`default`, and names the target `datasource`. This keeps data-residency +decisions in one place instead of scattered across object files. + +## Generating objects from existing tables + +You don't have to hand-write an object for every table. The fastest way +to bring a legacy schema into ObjectOS today is to **use a coding agent +(Claude Code) to scan the business tables and generate source-level +object definitions** — one `*.object.ts` file per table, in exactly the +shape the framework expects. + +The [`hotcrm`](https://github.com/objectstack-ai/hotcrm) reference app is +the canonical example of that shape: each table is a +`src/objects/.object.ts` file using `ObjectSchema.create({ … })` +with `Field.*` definitions, all assembled by `defineStack`. A typical +flow: + +1. **Connect** the business database as a datasource (above). +2. **Point Claude Code at the schema.** Ask it to introspect the + connected database — table names, columns, types, foreign keys — and + generate one `ObjectSchema.create` file per table, mapping SQL columns + to `Field.*` types and foreign keys to `Field.lookup(...)`. Set each + object's `datasource` to your connection (or rely on + `datasourceMapping`). +3. **Review & refine** the generated objects — add labels, field groups, + validations, and permissions. The output is ordinary source you own + and commit, just like `hotcrm/src/objects/*.object.ts`. +4. **Run.** The objects now read and write your existing tables through + the bound datasource. + +Because the generated objects are plain source, you get full control: +keep what fits, drop columns you don't want to expose, and layer +ObjectOS features (history tracking, activities, sharing rules) on top of +a database the platform never had to own. + +## Capability-aware query pushdown + +Each datasource advertises `DatasourceCapabilities` — whether it can +handle filters, sorting, pagination, aggregations, joins, full-text +search, transactions, and more. ObjectQL uses this to decide what to +**push down** to the database versus what to evaluate in memory: + +| Capability | Effect when supported | +|---|---| +| `queryFilters` | `WHERE` clauses run in the database | +| `querySorting` / `queryPagination` | `ORDER BY` / `LIMIT` run server-side | +| `queryAggregations` | `GROUP BY` / aggregates run server-side | +| `joins` | Related-object joins run server-side | +| `fullTextSearch` | Search hits a native index | +| `readOnly` | The connection rejects writes | + +A capable SQL datasource pushes nearly everything down; a limited or +read-only source gets the same query results, just with more work done in +the engine. You can override the advertised set per datasource via the +`capabilities` field when you know better than the driver default. + +## AI over connected data + +Once tables are modeled as objects, **the AI layer works on them for +free**. ObjectOS's agents and tools — `list_objects`, `describe_object`, +`query_records`, `aggregate_data`, and the data-chat agent — all go +through ObjectQL, which routes each object to its bound datasource. That +means: + +- A user can **ask questions in natural language** about data that lives + in the legacy business system, and the answer is computed against the + real rows. +- Tool calls and queries respect the **calling user's permissions** — + AI never sees more than the signed-in user is allowed to. +- The same agents, flows, and dashboards work whether an object is backed + by the primary database or an external business system. + +See [AI Service](/docs/configure/ai) and +[AI Agents](/docs/build/agents) for how to wire up the chat and agent +layers, and [Runtime](/docs/configure/runtime) for the primary-database +configuration that backs the `default` datasource. + +## Security notes + +- **Never inline credentials.** Pull host/user/password from environment + variables (or a secrets manager) as shown above. +- **Use read-only connections** for systems you don't intend to write to + — set the source `readOnly` capability (or a read-only DB user) so a + stray write can't reach production. +- **Scope with permissions.** Object- and field-level permissions apply + to connected data exactly as they do to native objects. + +## Roadmap: in-product External Datasource Federation + +The flow above — connect a database, model objects, generate them with a +coding agent — works today with shipped building blocks. A richer, +**turn-key federation** experience is in active design under +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(status: *Proposed*). Planned, **not yet shipped**: + +- A `schemaMode` (`managed` / `external` / `validate-only`) so ObjectOS + can bind to tables it does **not** own without trying to migrate them. +- An `external` binding sub-record on objects mapping object fields to + existing columns. +- An `os datasource introspect` / `validate` CLI to import a schema and + scaffold objects in one step. +- Boot-time and write-time **safety gates** for externally-owned schemas, + plus a Studio wizard for the whole flow. + +Until those land, prefer the documented path: declare the datasource, +bind objects (generated or hand-written), and verify against a +non-production copy first. + +## Where to go next + +- [Runtime](/docs/configure/runtime) — the primary database behind `default` +- [AI Service](/docs/configure/ai) — chat, embeddings, RAG, MCP +- [AI Agents](/docs/build/agents) — declarative agents over your objects +- [Objects](/docs/build/objects) — the `ObjectSchema.create` authoring surface +- [`examples/app-crm` datasources](https://github.com/objectstack-ai/framework/tree/main/examples/app-crm/src/datasources) — a real datasource + routing example diff --git a/content/docs/configure/data-sources.zh-Hans.mdx b/content/docs/configure/data-sources.zh-Hans.mdx new file mode 100644 index 0000000..3600f14 --- /dev/null +++ b/content/docs/configure/data-sources.zh-Hans.mdx @@ -0,0 +1,213 @@ +--- +title: 数据源 +description: 把 ObjectOS 接入你现有的业务数据库,路由对象,并让 AI 原生地查询这些数据。 +--- + +# 数据源 + +**数据源(datasource)** 是一个指向外部数据存储的具名连接。通过声明数据源, +你可以把 ObjectOS 指向企业本就在运行的数据库 —— 生产环境的 PostgreSQL、 +做报表的 MySQL 只读副本、MongoDB 集群 —— 然后把对象绑定到它们之上。对象一旦 +绑定,平台里的其它一切(REST/GraphQL API、权限、流程、仪表盘,以及 **AI Agent**) +都会统一地作用于这些数据,而不关心数据物理上存在哪里。 + +这是 ObjectOS 最实用的落地路径之一:你不必迁移遗留系统,而是连上它、把你关心 +的表建模成对象,再在**保持数据原地不动**的前提下,为它叠加 AI 原生能力 —— +对话、分析、自动化。 + +## 数据源是什么 + +每个数据源都是由 `DatasourceSchema` 校验的普通对象。核心字段: + +| 字段 | 用途 | +|---|---| +| `name` | 对象引用的唯一标识(`^[a-z_][a-z0-9_]*$`) | +| `label` | 可读的展示名 | +| `driver` | 处理连接的驱动(`postgres`、`mysql`、`sqlite`、`mongodb`、`memory`,或插件贡献的驱动) | +| `config` | 驱动相关的连接设置(host、database、凭据……) | +| `pool` | 连接池大小(`min`、`max`、超时) | +| `readReplicas` | 可选的只读副本配置 | +| `capabilities` | 覆盖驱动声明的可下推能力 | +| `healthCheck` | 存活探测的间隔/超时 | +| `active` | 连接是否启用 | + +已发布的驱动: + +| 驱动包 | `driver` | 后端 | +|---|---|---| +| `@objectstack/driver-sql` | `postgres`、`mysql`、`sqlite` | PostgreSQL、MySQL、SQLite(经 knex) | +| `@objectstack/driver-mongodb` | `mongodb` | MongoDB | +| `@objectstack/driver-memory` | `memory` | 进程内(测试、演示) | +| `@objectstack/driver-sqlite-wasm` | `sqlite`(WASM) | WebContainer / 浏览器中的 SQLite | + +## 声明数据源 + +数据源声明在 stack 上,并由 `defineStack` 汇编。把每个连接定义成一个带类型的 +`Datasource` 对象,再列入 `datasources`: + +```ts +// src/datasources/business.datasource.ts +import type { Datasource } from '@objectstack/spec'; + +// 连接一个【已有的】生产数据库。凭据来自环境变量 —— 切勿把密钥写死在源码里。 +export const BusinessDb: Datasource = { + name: 'business_primary', + label: 'Business System (Postgres)', + driver: 'postgres', + config: { + connection: { + host: process.env.BIZ_DB_HOST, + port: Number(process.env.BIZ_DB_PORT ?? 5432), + user: process.env.BIZ_DB_USER, + password: process.env.BIZ_DB_PASSWORD, + database: process.env.BIZ_DB_NAME, + }, + }, + pool: { min: 1, max: 10 }, + active: true, +}; +``` + +```ts +// objectstack.config.ts +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; +import { BusinessDb } from './src/datasources/business.datasource.js'; + +export default defineStack({ + manifest: { id: 'app.example.crm-extend', namespace: 'biz', version: '1.0.0' }, + datasources: [BusinessDb], + objects: Object.values(objects), +}); +``` + +> **不存在** `defineDatasource()` 这样的辅助函数。数据源就是你放进 `datasources` +> 数组里的一个 `Datasource` 对象 —— 与框架仓库里 `examples/app-crm` stack 的做法 +> 完全一致。 + +## 把对象绑定到数据源 + +每个对象都有 `datasource` 字段,默认是 `'default'`(主数据库)。把它设为你已连接 +的某个系统,即可路由特定对象: + +```ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Customer = ObjectSchema.create({ + name: 'biz_customer', + label: 'Customer', + datasource: 'business_primary', // ← 读写都走业务库 + fields: { + name: Field.text({ label: 'Name', required: true }), + email: Field.text({ label: 'Email' }), + tier: Field.select({ label: 'Tier', options: [/* … */] }), + }, +}); +``` + +### 用 `datasourceMapping` 做集中路由 + +逐个绑定对象适合少量场景。要路由整个命名空间或包,就在 stack 上声明一次路由规则。 +规则按顺序(或按 `priority`)求值,首个命中者生效: + +```ts +export default defineStack({ + datasources: [BusinessDb, AnalyticsReplica], + datasourceMapping: [ + { namespace: 'biz', datasource: 'business_primary' }, + { objectPattern: 'report_*', datasource: 'analytics_replica' }, + { package: 'com.example.logs', datasource: 'business_primary' }, + { default: true, datasource: 'default' }, + ], +}); +``` + +规则可按 `namespace`、`package`、`objectPattern`(glob)或 `default` 匹配,并指定 +目标 `datasource`。这样数据驻留的决策集中在一处,而不是散落在各个对象文件里。 + +## 从既有表生成对象 + +你不必为每张表手写对象。如今把遗留 schema 引入 ObjectOS 最快的方式,是**用编码 +Agent(Claude Code)扫描业务表并生成源码级的对象定义** —— 每张表一个 +`*.object.ts` 文件,形态正是框架所期望的。 + +[`hotcrm`](https://github.com/objectstack-ai/hotcrm) 参考应用就是这种形态的范例: +每张表是一个 `src/objects/.object.ts` 文件,用 `ObjectSchema.create({ … })` +加 `Field.*` 定义,全部由 `defineStack` 汇编。典型流程: + +1. **连接**业务数据库为数据源(见上)。 +2. **让 Claude Code 对准 schema。** 让它内省已连接的数据库 —— 表名、列、类型、 + 外键 —— 为每张表生成一个 `ObjectSchema.create` 文件,把 SQL 列映射到 `Field.*` + 类型、把外键映射到 `Field.lookup(...)`。给每个对象设好 `datasource`(或交给 + `datasourceMapping`)。 +3. **审阅与打磨**生成的对象 —— 补上 label、字段分组、校验与权限。产物就是你拥有 + 并提交的普通源码,和 `hotcrm/src/objects/*.object.ts` 一样。 +4. **运行。** 这些对象此刻便通过绑定的数据源读写你既有的表。 + +因为生成的对象是普通源码,你拥有完全的控制权:保留合适的、丢弃不想暴露的列,并在 +一个平台从不需要拥有的数据库之上叠加 ObjectOS 的能力(历史追踪、活动、共享规则)。 + +## 能力感知的查询下推 + +每个数据源都会声明 `DatasourceCapabilities` —— 是否支持过滤、排序、分页、聚合、 +连接、全文检索、事务等。ObjectQL 据此决定哪些**下推**到数据库、哪些在内存中求值: + +| 能力 | 支持时的效果 | +|---|---| +| `queryFilters` | `WHERE` 子句在数据库执行 | +| `querySorting` / `queryPagination` | `ORDER BY` / `LIMIT` 在服务端执行 | +| `queryAggregations` | `GROUP BY` / 聚合在服务端执行 | +| `joins` | 关联对象的连接在服务端执行 | +| `fullTextSearch` | 检索命中原生索引 | +| `readOnly` | 该连接拒绝写入 | + +一个能力完备的 SQL 数据源几乎能把所有操作下推;能力受限或只读的数据源得到相同的 +查询结果,只是引擎要多做一些工作。当你比驱动更清楚时,可通过 `capabilities` 字段 +按数据源覆盖其声明的能力集。 + +## 在已连接数据上用 AI + +一旦表被建模为对象,**AI 层就免费可用**。ObjectOS 的 Agent 与工具 —— +`list_objects`、`describe_object`、`query_records`、`aggregate_data` 以及 +data-chat agent —— 都经由 ObjectQL,后者会把每个对象路由到它绑定的数据源。这意味着: + +- 用户可以用**自然语言提问**那些存放在遗留业务系统里的数据,答案是针对真实记录 + 计算出来的。 +- 工具调用与查询遵守**调用者本人的权限** —— AI 永远看不到超出登录用户被允许范围 + 的内容。 +- 不论对象由主数据库还是外部业务系统支撑,同一批 Agent、流程、仪表盘都照常工作。 + +参见 [AI 服务](/docs/configure/ai) 与 [AI Agent](/docs/build/agents) 了解如何接好 +对话与 Agent 层,以及 [运行时](/docs/configure/runtime) 了解支撑 `default` 数据源 +的主数据库配置。 + +## 安全须知 + +- **切勿把凭据写死。** 像上面那样从环境变量(或密钥管理器)读取 host/user/password。 +- **对不打算写入的系统使用只读连接** —— 设置数据源的 `readOnly` 能力(或使用只读 + 的数据库账号),让误写无法触及生产。 +- **用权限收口。** 对象级与字段级权限对已连接数据的约束,与原生对象完全一致。 + +## 路线图:产品内置的外部数据源联邦 + +上面的流程 —— 连接数据库、建模对象、用编码 Agent 生成 —— 借助已发布的能力今天就 +能用。更完善的**一键式联邦**体验正在 +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +下积极设计中(状态:*Proposed*)。计划中、**尚未发布**的内容: + +- `schemaMode`(`managed` / `external` / `validate-only`),让 ObjectOS 能绑定到它 + **并不拥有**的表,而不去尝试迁移它们。 +- 对象上的 `external` 绑定子记录,把对象字段映射到既有列。 +- `os datasource introspect` / `validate` CLI,一步导入 schema 并脚手架出对象。 +- 面向外部所属 schema 的启动期与写入期**安全闸门**,以及覆盖整个流程的 Studio 向导。 + +在它们落地之前,请优先采用已记录的路径:声明数据源、绑定对象(生成的或手写的), +并先在非生产副本上验证。 + +## 下一步去哪 + +- [运行时](/docs/configure/runtime) —— `default` 背后的主数据库 +- [AI 服务](/docs/configure/ai) —— 对话、嵌入、RAG、MCP +- [AI Agent](/docs/build/agents) —— 作用于你对象之上的声明式 Agent +- [对象](/docs/build/objects) —— `ObjectSchema.create` 的编写界面 +- [`examples/app-crm` 数据源](https://github.com/objectstack-ai/framework/tree/main/examples/app-crm/src/datasources) —— 一个真实的数据源 + 路由示例 diff --git a/content/docs/configure/email.ko.mdx b/content/docs/configure/email.ko.mdx new file mode 100644 index 0000000..d92c4f9 --- /dev/null +++ b/content/docs/configure/email.ko.mdx @@ -0,0 +1,82 @@ +--- +title: 이메일 +description: 트랜잭션 이메일 전송 제공자와 템플릿을 구성합니다. +--- + +# 이메일 + +ObjectOS는 애플리케이션에서 필요할 때(비밀번호 재설정, 초대, 승인 +알림, 예약 보고서) 프레임워크의 이메일 플러그인을 통해 트랜잭션 +이메일을 전송합니다. 플러그인에는 세 가지 전송 방식이 함께 제공됩니다. + +## 전송 방식 + +| 제공자 | 사용 시점 | 필요한 env | +|---|---|---| +| `log` | 로컬 개발용. 이메일을 전송하지 않고 stdout에 로깅합니다 | 없음 | +| `resend` | Resend를 통한 SaaS 전달성 | `OS_EMAIL_API_KEY` | +| `postmark` | Postmark를 통한 SaaS 전달성 | `OS_EMAIL_API_KEY` | + +기본값은 `log`입니다. 실제 제공자가 구성되어 있지만 API 키가 제공되지 +않은 경우 ObjectOS는 log 전송 방식으로 폴백합니다. 이는 비프로덕션 +환경에서 실수로 메일이 전송되는 것을 방지하는 데 유용합니다. + +### SMTP는 어떤가요? + +네이티브 SMTP 전송 방식은 현재 런타임에 **내장되어 있지 않습니다**. +환경에서 SMTP가 필요한 경우(기업 릴레이, 온프레미스 메일, 에어갭 +배포) 두 가지 프로덕션급 옵션이 있습니다. + +1. **SMTP-to-API 릴레이를 ObjectOS 앞단에서 실행합니다.** Resend, + Postmark, 그리고 자체 호스팅 대안(Postal, Cuttlefish)은 모두 SMTP + 인그레스를 수용하고 자체 HTTP API를 통해 재전송합니다. ObjectOS는 + 평소처럼 HTTPS로 이들과 통신합니다. +2. **사용자 정의 이메일 플러그인으로 런타임을 실행합니다.** 이메일 + 플러그인 API는 작습니다(하나의 `send(message)` 함수). `nodemailer`를 + 래핑하는 프로젝트 플러그인은 `requires` 목록을 통해 연결됩니다. + spec 저장소의 [플러그인 작성 가이드](https://github.com/objectstack-ai/spec)를 + 참고하세요. + +네이티브 SMTP 전송 방식은 로드맵에 있습니다. 진행 상황은 +[github.com/objectstack-ai/objectos/issues](https://github.com/objectstack-ai/objectos/issues)에서 +확인하세요. + +## 환경 변수 + +| 변수 | 용도 | +|---|---| +| `OS_EMAIL_PROVIDER` | `log`, `resend`, 또는 `postmark` | +| `OS_EMAIL_API_KEY` | 제공자 API 키(Resend 또는 Postmark) | +| `OS_EMAIL_FROM` | 기본 발신 주소. `addr@x`와 `Name ` 형식을 모두 지원합니다 | +| `OS_EMAIL_RETRIES` | 전송 실패 시 전송 재시도 횟수(기본값 `0`) | + +환경 변수는 아티팩트의 `email` 구성 블록에서 일치하는 값을 +재정의하므로, 운영자는 아티팩트를 다시 빌드하지 않고도 전송을 다시 +지정할 수 있습니다. + +## 템플릿 + +재사용 가능한 템플릿은 `sys_email_template`에 저장됩니다. 템플릿은 +프레임워크의 템플릿 엔진이 평가하는 변수 보간을 지원합니다. +애플리케이션 코드(또는 플로우)는 템플릿 id와 변수 번들로 이메일 +서비스를 호출합니다. 서비스는 제목/본문을 구체화하여 구성된 전송 +방식에 전달합니다. + +## 전달 확인 + +Resend / Postmark의 경우, 발신 도메인이 제공자 대시보드에 구성되어 +있는지(SPF, DKIM, 선택적으로 DMARC) 확인하세요. 가장 빠른 엔드투엔드 +점검 방법은 이메일 설정 페이지의 Console **Send test email** 작업입니다. +이는 실제 전송 방식을 사용하며 전송 오류를 인라인으로 표시합니다. + +## 운영 지침 + +- API 키를 비밀로 취급하세요. 아티팩트가 아닌 고객의 시크릿 매니저에 + 저장하세요. +- 전송 오류 로그를 주시하세요. 제공자 속도 제한, 차단(suppression), + 반송(bounce)이 모두 여기에 표시됩니다. +- 감사에 민감한 트랜잭션 메일(비밀번호 재설정, MFA 챌린지)은 고객의 + 정책에 따라 보존해야 합니다. 보존 기간은 전송 방식이 아닌 감사 + 로그에 설정하세요. +- 발신 메일은 비즈니스 트랜잭션을 차단하지 않습니다. 전송 실패는 + 오류로 표시되지만, 원본 레코드 변경을 롤백하지는 않습니다. diff --git a/content/docs/configure/meta.de.json b/content/docs/configure/meta.de.json index b898a36..2d6493d 100644 --- a/content/docs/configure/meta.de.json +++ b/content/docs/configure/meta.de.json @@ -3,6 +3,7 @@ "defaultOpen": false, "pages": [ "runtime", + "data-sources", "authentication", "permissions", "storage", diff --git a/content/docs/configure/meta.es.json b/content/docs/configure/meta.es.json index a5bd209..7aef632 100644 --- a/content/docs/configure/meta.es.json +++ b/content/docs/configure/meta.es.json @@ -3,6 +3,7 @@ "defaultOpen": false, "pages": [ "runtime", + "data-sources", "authentication", "permissions", "storage", diff --git a/content/docs/configure/meta.fr.json b/content/docs/configure/meta.fr.json index 0882fb9..7db55c6 100644 --- a/content/docs/configure/meta.fr.json +++ b/content/docs/configure/meta.fr.json @@ -3,6 +3,7 @@ "defaultOpen": false, "pages": [ "runtime", + "data-sources", "authentication", "permissions", "storage", diff --git a/content/docs/configure/meta.ja.json b/content/docs/configure/meta.ja.json index 35e1060..514da0f 100644 --- a/content/docs/configure/meta.ja.json +++ b/content/docs/configure/meta.ja.json @@ -3,6 +3,7 @@ "defaultOpen": false, "pages": [ "runtime", + "data-sources", "authentication", "permissions", "storage", diff --git a/content/docs/configure/meta.json b/content/docs/configure/meta.json index 0098cf0..b2a622c 100644 --- a/content/docs/configure/meta.json +++ b/content/docs/configure/meta.json @@ -1,5 +1,5 @@ { "title": "Configure", "defaultOpen": false, - "pages": ["runtime", "authentication", "permissions", "storage", "ai", "system-settings", "api-access", "webhooks", "email"] + "pages": ["runtime", "data-sources", "authentication", "permissions", "storage", "ai", "system-settings", "api-access", "webhooks", "email"] } diff --git a/content/docs/configure/meta.ko.json b/content/docs/configure/meta.ko.json new file mode 100644 index 0000000..38b0529 --- /dev/null +++ b/content/docs/configure/meta.ko.json @@ -0,0 +1,16 @@ +{ + "title": "설정", + "defaultOpen": false, + "pages": [ + "runtime", + "data-sources", + "authentication", + "permissions", + "storage", + "ai", + "system-settings", + "api-access", + "webhooks", + "email" + ] +} diff --git a/content/docs/configure/meta.zh-Hans.json b/content/docs/configure/meta.zh-Hans.json index ec333d5..d2dc0b0 100644 --- a/content/docs/configure/meta.zh-Hans.json +++ b/content/docs/configure/meta.zh-Hans.json @@ -1,5 +1,5 @@ { "title": "配置", "defaultOpen": false, - "pages": ["runtime", "authentication", "permissions", "storage", "ai", "system-settings", "api-access", "webhooks", "email"] + "pages": ["runtime", "data-sources", "authentication", "permissions", "storage", "ai", "system-settings", "api-access", "webhooks", "email"] } diff --git a/content/docs/configure/permissions/index.ko.mdx b/content/docs/configure/permissions/index.ko.mdx new file mode 100644 index 0000000..43bf125 --- /dev/null +++ b/content/docs/configure/permissions/index.ko.mdx @@ -0,0 +1,129 @@ +--- +title: 권한 +description: 신원, 역할, 권한 집합, 레코드 접근, 필드 보안 — 전체 접근 모델을 한 페이지에서. +--- + +# 권한 + +ObjectOS는 엔터프라이즈 소프트웨어에서 20년간 검증된 방식을 차용한 계층형 접근 모델을 갖추고 있습니다: 신원 → 역할 → 권한 집합 → 레코드 접근 → 필드 보안. 각 계층은 서로 다른 질문에 답하며, 필요하지 않은 계층은 무시할 수 있습니다. + +## 하나의 다이어그램으로 보는 모델 + +```text +Authentication Who is the caller? + ↓ +Identity Which user/org/membership is active? + ↓ +Roles Where do they sit in the hierarchy? + ↓ +Permission sets What CAN they do — apps, objects, fields, system? + ↓ +Record access WHICH records can they touch? + ↓ +Field security For those records, which FIELDS are readable / writable? +``` + +각 계층은 보안 플러그인에 의해 적용됩니다. 단순한 앱이라면 권한 집합만 사용하고(역할이나 공유 규칙 없이), 요구 사항이 생길 때 나머지를 추가할 수 있습니다. + +## 계층 1 — 신원 + +신원 객체는 프로젝트 데이터베이스에 존재합니다. 가장 중요한 것들은 다음과 같습니다: + +| 객체 | 의미 | +|---|---| +| `sys_user` | 인증할 수 있는 사람 또는 서비스 계정 | +| `sys_organization` | 테넌트 / 워크스페이스 경계(멀티테넌트 앱) | +| `sys_member` | 조직 내 사용자의 멤버십(역할은 멤버십 단위로 할당됨) | +| `sys_department`, `sys_team` | 공유 규칙을 위한 선택적 조직 구조 | +| `sys_invitation` | 수락을 기다리는 대기 중인 초대 | +| `sys_session` | 활성 인증 세션 | +| `sys_api_key` | 사용자에 바인딩된 장기 프로그래밍 자격 증명 | + +멀티테넌트 배포에서는: + +- 사용자는 프로젝트 데이터베이스 범위로 한정됩니다. +- 세션은 프로젝트 호스트명 범위로 한정됩니다. +- 행 수준 검사는 현재 사용자의 조직과 권한을 사용합니다. +- 컨트롤 플레인 사용자는 자동으로 비즈니스 사용자가 **아닙니다** — 플랫폼 SSO 또는 명시적 프로비저닝을 통해 매핑되어야 합니다. + +이들은 런타임에 **Console**(`/_console/`)에서 생성/관리하거나, 새 환경을 위해 `objectstack.config.ts`에 시드할 수 있습니다. + +## 계층 2 — 역할 + +역할은 조직도(CFO → Finance Manager → Analyst)를 모델링합니다. 역할이 존재하는 주된 이유는 공유 규칙과 보고서가 "레코드 소유자의 관리자"와 같은 표현을 할 수 있게 하기 위함입니다. 계층적 접근이 필요할 때 역할을 사용하고, 평면적인 팀에서는 생략하세요. + +[역할](/docs/configure/permissions/roles)을 참조하세요. + +## 계층 3 — 권한 집합 + +권한 집합은 기능을 부여하는 주요 방법입니다. 사용자에게 직접 연결되거나 역할을 통해 연결됩니다. + +### 무엇을 부여하는가 + +| 유형 | 예시 | +|---|---| +| 애플리케이션 접근 | CRM 열기, 지원 포털 열기 | +| 객체 권한 | 객체의 레코드 생성 / 읽기 / 수정 / 삭제 | +| 필드 권한 | 특정 필드 읽기 또는 수정 | +| 시스템 권한 | Console 접근, 보고서 실행, 데이터 내보내기, 감사 로그 보기 | +| 통합 권한 | API 키 사용, 웹훅 구성, 관리 작업 실행 | + +### 객체 권한 플래그 + +다음은 보안 플러그인이 검사하는 정확한 플래그 이름입니다: + +| 플래그 | 의미 | +|---|---| +| `allowRead` | 레코드 접근을 통해 사용자가 볼 수 있는 레코드 읽기 | +| `allowCreate` | 새 레코드 생성 | +| `allowEdit` | 사용자가 볼 수 있는 레코드 수정 | +| `allowDelete` | 사용자가 볼 수 있는 레코드 삭제 | +| `viewAllRecords` | 레코드 접근 규칙을 무시하고 해당 객체의 **모든** 레코드 읽기 | +| `modifyAllRecords` | **모든** 레코드 수정/삭제; `viewAllRecords`를 함의함 | + +`viewAllRecords`와 `modifyAllRecords`는 해당 객체에 대한 테넌트 전역 슈퍼유저 권한입니다. 명시적인 관리용 권한 집합에만 한정하고, 사용자 대상 역할에서는 제외하세요. + +[권한 집합](/docs/configure/permissions/permission-sets)을 참조하세요. + +## 계층 4 — 레코드 접근 + +`viewAllRecords`가 **없는** 사용자의 경우, 어떤 행을 볼 수 있을까요? + +이 모델은 다음을 지원합니다: + +- 암묵적 소유권(사용자가 생성했거나 소유한 행) +- 공유 규칙(선언적 — "팀 A는 팀 A의 레코드를 본다") +- 명시적 공유(`sys_record_share` 행 — 일회성 공유) +- 조직 범위 지정(행의 `organization_id`가 사용자의 조직과 일치) + +[레코드 접근](/docs/configure/permissions/record-access)을 참조하세요. + +## 계층 5 — 필드 보안 + +사용자가 레코드를 볼 수 있는 경우에도, 개별 필드는 다음과 같이 설정될 수 있습니다: + +- **숨김** — 필드가 API 및 UI 응답에서 제거됩니다. +- **읽기 전용** — 필드는 반환되지만 쓰기 시 거부됩니다. + +필드 보안은 객체별 + 권한 집합별로 적용됩니다. 일반적인 사용 예: + +- HR 외부의 누구에게나 `sys_user`의 `salary`를 숨김. +- 지원 담당자에게 `external_account_id`를 읽을 수는 있지만 수정할 수는 없게 설정. + +이는 객체 권한과 동일한 평가기에서 적용되므로 REST, ObjectQL, Console 전반에 걸쳐 일관되게 작동합니다. + +## 어디서 시작할까 + +| 무엇을 만들고 있다면 … | 사용 | +|---|---| +| 단일 팀 내부 도구 | 권한 집합만 | +| 관리자가 보고서를 보는 멀티팀 앱 | 역할 + 권한 집합 | +| 멀티테넌트 SaaS 형태의 앱 | 조직 범위 지정 + 권한 집합 | +| PII가 있는 규제 대상 앱 | 그 위에 필드 보안 추가 | +| 복잡한 CRM 스타일 앱 | 전체 스택 | + +## 진단 및 감사 + +- `/_console/`는 평가되는 대로 모든 사용자의 유효 권한을 보여줍니다. +- 감사 로그(`sys_audit_log`)는 권한에 민감한 변경 사항(권한 부여, 역할 할당, 권한 집합 편집)을 기록합니다. +- 거부된 요청은 실패한 규칙(객체 권한, 레코드 접근, 필드 보안 중 무엇인지)을 기록하므로, 지원 담당자가 "왜 이걸 볼 수 없나요?"라는 질문에 빠르게 답할 수 있습니다. diff --git a/content/docs/configure/permissions/meta.ko.json b/content/docs/configure/permissions/meta.ko.json new file mode 100644 index 0000000..87f56be --- /dev/null +++ b/content/docs/configure/permissions/meta.ko.json @@ -0,0 +1,9 @@ +{ + "title": "권한", + "pages": [ + "index", + "roles", + "permission-sets", + "record-access" + ] +} diff --git a/content/docs/configure/permissions/permission-sets.ko.mdx b/content/docs/configure/permissions/permission-sets.ko.mdx new file mode 100644 index 0000000..29f5619 --- /dev/null +++ b/content/docs/configure/permissions/permission-sets.ko.mdx @@ -0,0 +1,110 @@ +--- +title: 권한 집합(Permission Sets) +description: 애플리케이션, 객체, 필드, 시스템 권한을 부여합니다. +--- + +# 권한 집합(Permission Sets) + +권한 집합은 기능을 부여하는 기본적인 방법입니다. 사용자에게 직접 할당하거나 +[역할](/docs/configure/permissions/roles)을 통해 간접적으로 할당할 수 있습니다. + +## 권한 집합이 제어하는 항목 + +| 권한 유형 | 예시 | +|---|---| +| 애플리케이션 접근 | 사용자가 CRM 또는 Console을 열 수 있음 | +| 객체 권한 | 레코드 생성, 읽기, 수정, 삭제 | +| 필드 권한 | 선택한 필드에 대한 읽기 또는 수정 | +| 시스템 권한 | Console 접근, 보고서 실행, 데이터 내보내기 | +| 통합 권한 | API 키, 웹훅 또는 관리자 작업 사용 | + +## 객체 권한 + +객체 권한은 다음 질문에 답합니다. + +```text +Can this user perform this operation on this object at all? +``` + +권한 집합 부여는 다음 플래그 이름을 사용합니다(보안 플러그인의 권한 평가기에서 +적용되는 방식과 동일). + +| 플래그 | 의미 | +|---|---| +| `allowRead` | 레코드 접근 규칙에 의해 허용된 레코드 읽기 | +| `allowCreate` | 레코드 생성 | +| `allowEdit` | 레코드 접근 규칙에 의해 허용된 레코드 수정 | +| `allowDelete` | 레코드 접근 규칙에 의해 허용된 레코드 삭제 | +| `viewAllRecords` | 레코드 접근을 우회하여 해당 객체의 모든 레코드 읽기 | +| `modifyAllRecords` | 레코드 접근을 우회하여 해당 객체의 모든 레코드 수정 또는 삭제. 읽기에 대해 `viewAllRecords`를 암묵적으로 포함 | + +`viewAllRecords` 또는 `modifyAllRecords`가 일반적인 행 수준 경계를 우회하지 않는 +한 [레코드 접근](/docs/configure/permissions/record-access)은 여전히 중요합니다. +이러한 플래그는 관리용 권한 집합에만 사용하세요. 이는 사실상 해당 객체에 대한 +테넌트 전역 슈퍼유저 권한 부여와 같습니다. + +## 시스템 권한 + +시스템 권한은 다음과 같은 플랫폼 작업을 위한 것입니다. + +- Console 접근; +- 사용자 관리; +- 역할 및 권한 집합 관리; +- 보고서 실행; +- 보고서 내보내기; +- 통합 관리; +- 감사 로그 보기. + +관리자가 쉽게 감사할 수 있도록 시스템 권한을 비즈니스 객체 권한과 분리하여 +유지하세요. + +## 권장 집합 + +몇 가지 명명된 권한 집합으로 시작하세요. + +| 권한 집합 | 목적 | +|---|---| +| Basic User | 로그인 및 메인 애플리케이션 접근 | +| Setup Administrator | 사용자, 역할, 설정 및 진단 관리 | +| Report Viewer | 보고서 및 대시보드 보기 | +| Integration Operator | 웹훅/API 키 관리 | +| Support User | 지원 객체 읽기/수정 | + +그런 다음 고객의 애플리케이션에 맞는 도메인별 집합을 추가하세요. + +## 필드 보안(부록) + +권한 집합은 필드 수준의 권한도 포함합니다. 사용자가 레코드를 읽을 수 있더라도 +개별 필드를 숨기거나 읽기 전용으로 만들 수 있습니다. + +### 두 가지 모드 + +| 모드 | 효과 | +|---|---| +| **숨김** | 필드가 API 응답과 Console 뷰에서 완전히 제거됨 | +| **읽기 전용** | 필드가 반환되지만 쓰기는 거부됨 | + +### 적용 위치 + +객체 권한을 확인하는 동일한 평가기가 모든 경로(REST, ObjectQL, 생성된 Console +폼, 내보내기)에서 필드 규칙을 적용합니다. 하위 수준 API를 통한 "뒷문"은 +존재하지 않습니다. + +### 일반적인 패턴 + +| 사용 사례 | 권장 사항 | +|---|---| +| `sys_user`의 HR 데이터(급여, SSN) | `HR` 권한 집합을 제외한 모든 사용자에게 숨김 | +| 외부 시스템 식별자 | 지원 담당자에게는 읽기 전용, 통합 운영자에게는 쓰기 가능 | +| `product`의 내부 원가 vs. 고객 가격 | 영업에게는 `cost`를 숨기고 재무 부서에 노출 | +| PHI가 포함된 메모 필드 | 권한 집합에 해당 임상 역할이 포함되지 않는 한 숨김 | + +### 작성 지침 + +- 필드에 민감한 데이터가 포함된 경우 읽기 전용보다 **숨김**을 기본값으로 + 사용하세요. 읽기 전용은 여전히 응답과 로그에 값을 노출합니다. +- 필드별로 하나의 집합을 만드는 대신, 실제 역할(`HR Manager`, + `Finance Read-only`)에 맞는 권한 집합에 필드 규칙을 묶으세요. 감사가 더 + 쉬워집니다. +- 규정 준수 사용 사례의 경우, 사후에 "누가 이 행을 보았는가?"에 답할 수 있도록 + 필드 보안을 감사 로그 보존과 함께 사용하세요. diff --git a/content/docs/configure/permissions/record-access.ko.mdx b/content/docs/configure/permissions/record-access.ko.mdx new file mode 100644 index 0000000..d1f3a8e --- /dev/null +++ b/content/docs/configure/permissions/record-access.ko.mdx @@ -0,0 +1,60 @@ +--- +title: 레코드 접근 +description: 사용자가 어떤 레코드를 보거나 수정할 수 있는지 제어합니다. +--- + +# 레코드 접근 + +레코드 접근은 객체 권한이 작업을 허용한 후, 사용자가 어떤 행을 보거나 수정할 수 +있는지 제어합니다. + +## 메커니즘 + +| 메커니즘 | 목적 | +|---|---| +| 행 수준 보안 | 테넌트 또는 조직 격리 적용 | +| 공유 규칙 | 선언적 기준에 따라 접근 권한 부여 | +| 레코드 공유 | 특정 사용자, 역할 또는 그룹에 접근 권한 부여 | +| 소유권/계층 구조 | 소유자 또는 관리자 구조를 통해 접근 권한 부여 | + +## 기본 테넌트 격리 + +보안 플러그인은 현재 사용자의 컨텍스트를 통해 테넌트 격리를 적용합니다. 표준 +플랫폼 객체의 경우, 기본 규칙이 일반적인 조직 필드를 포함하지 않는 전역 +테이블을 보호합니다. + +실제 고객의 기대는 다음과 같습니다. + +```text +Users only see records that belong to their organization or records +explicitly shared with them. +``` + +## 공유 규칙 + +반복 가능한 비즈니스 정책에는 공유 규칙을 사용하세요. + +- 모든 지역 관리자는 자신의 지역에 있는 계정을 읽을 수 있습니다. +- 지원 관리자는 에스컬레이션된 케이스를 볼 수 있습니다. +- 재무 부서는 승인된 인보이스를 읽을 수 있습니다. +- 감사자는 감사 검토용으로 태그된 레코드를 읽을 수 있습니다. + +## 레코드 공유 + +예외 상황에는 레코드 공유를 사용하세요. + +- 하나의 기회를 전문가와 공유합니다. +- 에스컬레이션 중에 임시 접근 권한을 부여합니다. +- 외부 통합에 특정 레코드에 대한 접근 권한을 부여합니다. + +## 가시성 문제 해결 + +사용자가 레코드를 볼 수 없을 때는 다음 순서로 확인하세요. + +1. 사용자가 인증되었으며 예상한 조직에 속해 있습니까? +2. 사용자에게 객체 `read` 권한이 있습니까? +3. 행 수준 보안이 해당 레코드를 허용합니까? +4. 적용되어야 할 공유 규칙이 있습니까? +5. 직접 레코드 공유가 있습니까? +6. 레코드가 예상한 계층 구조 내의 사용자 또는 역할이 소유하고 있습니까? +7. 사용자가 올바른 프로젝트/호스트명을 보고 있습니까? diff --git a/content/docs/configure/permissions/roles.ko.mdx b/content/docs/configure/permissions/roles.ko.mdx new file mode 100644 index 0000000..af5cd69 --- /dev/null +++ b/content/docs/configure/permissions/roles.ko.mdx @@ -0,0 +1,62 @@ +--- +title: 역할 +description: 역할을 통한 모델 계층 구조와 관리 책임. +--- + +# 역할 + +역할은 조직 내에서 사용자의 위치를 나타냅니다. 계층 구조와 관리 책임을 +표현하는 데 유용하지만, 권한을 부여하는 유일한 수단이 되어서는 안 됩니다. + +## 역할의 책임 + +다음을 모델링하는 데 역할을 사용하세요. + +- 관리자/하위자 관계; +- 부서 또는 팀 계층 구조; +- 소유권 계층 구조에 기반한 레코드 가시성; +- 승인 책임; +- 보고 체계. + +구체적인 객체, 필드, 시스템 기능을 부여하려면 +[권한 집합](/docs/configure/permissions/permission-sets)을 사용하세요. + +## 시스템 객체 + +| 객체 | 용도 | +|---|---| +| `sys_role` | 역할 정의 및 계층 구조 | +| `sys_role_permission_set` | 역할을 통해 할당된 권한 집합 | +| `sys_member` | 사용자의 조직 멤버십 및 역할 컨텍스트 | + +## 권장 패턴 + +작은 역할 계층 구조로 시작하세요. + +```text +System Administrator +Sales Director +Sales Manager +Sales Representative +Support Manager +Support Agent +``` + +그런 다음 기능을 위한 권한 집합을 연결하세요. + +```text +Sales Manager role + -> CRM User + -> Sales Manager Access + -> Report Viewer +``` + +이렇게 하면 권한이 발전하는 동안에도 역할 트리를 안정적으로 유지할 수 있습니다. + +## 피해야 할 사항 + +- 개별 사용자마다 하나의 역할을 만드는 것; +- 모든 역할 안에 동일한 권한을 중복으로 넣는 것; +- 역할 이름을 비즈니스 로직 검사에 사용하는 것; +- [레코드 공유](/docs/configure/permissions/record-access)가 더 정밀할 수 있는 상황에서 + 관리자 역할을 통해 광범위한 접근 권한을 부여하는 것. diff --git a/content/docs/configure/runtime.ko.mdx b/content/docs/configure/runtime.ko.mdx new file mode 100644 index 0000000..203040b --- /dev/null +++ b/content/docs/configure/runtime.ko.mdx @@ -0,0 +1,126 @@ +--- +title: 런타임 구성 +description: 아티팩트 로딩, 프로젝트 해석, 데이터베이스, 캐시, 런타임 시크릿을 구성합니다. +--- + +# 런타임 구성 + +ObjectOS 런타임 구성은 세 가지 질문에 답합니다. + +1. 이 요청은 어떤 프로젝트를 처리해야 하는가? +2. 컴파일된 아티팩트는 어디에 있는가? +3. 프로젝트는 어떤 데이터베이스와 런타임 서비스를 사용해야 하는가? + +## 부팅 모드 + +| 모드 | 사용 시점 | 주요 구성 | +|---|---|---| +| Standalone | 구성 없이 `os start` — 빠른 데모, 빈 커널, 약 23개의 플러그인, `~/.objectstack/data/standalone.db`의 로컬 SQLite | _없음_ (기본값) | +| File-backed | 단일 프로젝트, 데모, 고객 오프라인 번들, 망분리 배포 | `OS_ARTIFACT_FILE` | +| Cloud-connected | 호스팅 또는 프라이빗 컨트롤 플레인이 프로젝트 아티팩트를 게시 | `OS_CLOUD_URL`, `OS_PROJECT_ID` 또는 호스트명 해석 | + +### Standalone 모드 + +구성이나 아티팩트 없이 `os start`를 실행할 때의 기본값입니다. +ObjectOS는 플랫폼 플러그인(`auth`, `security`, `audit`, `storage`, +`webhooks`, `mcp-server`, `marketplace-proxy`, +`marketplace-install-local`, …)이 로드된 빈 커널을 부팅하고, +`~/.objectstack/data/standalone.db`의 로컬 SQLite를 열며, Console과 +Account UI를 제공합니다. + +Console의 marketplace 탭에서 앱을 설치하여 커널을 객체, 뷰, 플로우로 +채우세요 — 재빌드나 재시작이 필요 없습니다. 데모, 평가, 그리고 "이게 무엇을 +하는지 보여줘" 식의 탐색에 가장 적합합니다. + +`os start`는 자동으로 단계를 격상합니다. + +| 감지된 것 | 동작 | +|---|---| +| 없음 | Standalone 모드 (위 참조) | +| cwd의 `objectstack.config.ts` | Project 모드 — 자동 컴파일, `HOME=/.objectstack` | +| cwd의 컴파일된 아티팩트 | Artifact 모드 — 해당 아티팩트 로드 | +| 명시적 `--artifact ` 또는 `OS_CLOUD_URL` | File-backed / cloud-connected 모드 | + +## File-backed 모드 + +설정: + +```bash +OS_ARTIFACT_FILE=/artifacts/objectstack.json +``` + +ObjectOS는 로컬 Artifact API 클라이언트를 사용합니다. 모든 호스트명은 동일한 +프로젝트로 해석됩니다. 런타임 구성은 아티팩트에 존재하면 그곳에서 읽으며, +없으면 평가용 로컬 런타임 기본값으로 폴백합니다. + +`OS_ARTIFACT_FILE`은 ObjectOS 앱 래퍼 규칙입니다. 기반 런타임은 +`OS_ARTIFACT_PATH`도 직접 허용합니다(`@objectstack/cli`의 `dev` 및 `start` +명령과 프레임워크의 standalone 스택에서 사용). 둘 중 하나만 설정하세요 — +둘 다 설정하지 마세요. + +선택 사항: + +```bash +OS_ENVIRONMENT_ID=env_prod # or the legacy alias OS_PROJECT_ID +OS_WATCH_ARTIFACT=1 +``` + +watch 모드는 개발이나 스모크 테스트에만 사용하세요. + +## Cloud-connected 모드 + +설정: + +```bash +OS_CLOUD_URL=https://cloud.example.com +OS_CLOUD_API_KEY=replace-with-deployment-token +``` + +ObjectOS는 컨트롤 플레인에 다음을 요청합니다. + +- 호스트명을 프로젝트/환경으로 해석; +- 현재 아티팩트를 가져오기; +- 해당 프로젝트의 런타임 데이터베이스 구성을 수신. + +ObjectOS는 컨트롤 플레인 데이터베이스에 직접 연결해서는 안 됩니다. + +## 인증 시크릿 + +강력한 베이스 시크릿을 설정하세요: + +```bash +OS_AUTH_SECRET=replace-with-a-strong-random-secret +``` + +ObjectOS는 이 값으로부터 프로젝트별 인증 시크릿을 파생합니다. 이를 통해 +세션 서명을 프로젝트별로 격리하면서도 결정론적 재시작을 허용합니다. 이 값을 +교체하면 기존 세션이 무효화됩니다. + +## 커널 및 아티팩트 캐시 + +ObjectOS는 모든 요청마다 커널을 재빌드하지 않도록 해석된 환경과 프로젝트 +커널을 캐시합니다. + +| 변수 | 기본값 | 용도 | +|---|---:|---| +| `OS_KERNEL_CACHE_SIZE` | `32` | 캐시되는 프로젝트 커널의 최대 개수 | +| `OS_KERNEL_TTL_MS` | `900000` | 캐시된 커널의 유휴 TTL | +| `OS_ENV_CACHE_TTL_MS` | `300000` | 호스트명/환경 해석 캐시 | +| `OS_ARTIFACT_CACHE_TTL_MS` | `300000` | 아티팩트 응답 캐시 | + +TTL을 낮추면 변경 사항이 더 빠르게 반영됩니다. TTL을 높이면 컨트롤 플레인 +트래픽과 콜드 스타트가 줄어듭니다. + +## 데이터베이스 구성 + +cloud-connected 모드에서는 컨트롤 플레인이 아티팩트 응답과 함께 프로젝트별 +런타임 데이터베이스 구성을 반환합니다. + +file-backed 모드에서는 ObjectOS가 아티팩트의 데이터소스 선언으로부터 +데이터베이스 설정을 파생할 수 있습니다. 지원되는 프레임워크 드라이버에는 +SQLite, PostgreSQL, MySQL, MongoDB, 그리고 메모리 기반 평가 드라이버가 +포함됩니다. + +프로덕션에서는 고객이 관리하는 데이터베이스를 사용하세요. 배포가 명시적으로 +단일 노드 평가인 경우가 아니라면 비즈니스 데이터를 컨테이너 로컬 스토리지에 +의존하지 마세요. diff --git a/content/docs/configure/storage.ko.mdx b/content/docs/configure/storage.ko.mdx new file mode 100644 index 0000000..b736aed --- /dev/null +++ b/content/docs/configure/storage.ko.mdx @@ -0,0 +1,189 @@ +--- +title: 스토리지 +description: ObjectOS가 파일을 저장하는 위치 — 로컬 디스크, S3, R2, MinIO, Spaces. +--- + +# 스토리지 + +ObjectOS 파일(첨부 파일, 업로드, 생성된 문서)은 +**스토리지 서비스**를 통해 흐릅니다 — 두 가지 어댑터를 갖춘 플러그형 추상화 계층으로, +**로컬 파일 시스템**(기본값)과 **S3 호환**(프로덕션)을 제공합니다. + +이 서비스는 `@objectstack/service-storage`에서 제공되며 +standalone 및 프로젝트 부팅 시 기본적으로 활성화됩니다. + +## 사용자가 상호작용하는 방식 + +| 인터페이스 | 동작 | +|---|---| +| Console 파일/이미지 필드 | 브라우저가 presigned URL을 통해 스토리지로 직접 업로드 | +| REST `/api/v1/storage/*` | 프로그래밍 방식의 업로드/다운로드 엔드포인트 | +| 객체 `attachment` / `image` 필드 | 업로드 위젯으로 렌더링되며, 메타데이터는 `sys_file`에 유지됨 | + +파일은 레코드에 원시 경로로 저장되는 것이 아니라 `sys_file` 시스템 객체에서 추적됩니다. +이를 통해 데이터 모델이 스토리지 백엔드로부터 분리됩니다. + +## 로컬 파일 시스템 (기본값) + +적합한 용도: 개발, 단일 노드 배포, 데모. + +```ts +// objectstack.config.ts (or wherever you assemble plugins) +import { StorageServicePlugin } from '@objectstack/service-storage'; + +new StorageServicePlugin({ + adapter: 'local', + local: { + rootDir: './uploads', + baseUrl: 'http://localhost:3000', // for presigned URLs + signingSecret: process.env.OS_STORAGE_SIGNING_SECRET, // optional; auto-generated if omitted + }, + presignedTtl: 3600, // seconds — TTL for presigned URLs + sessionTtl: 86400, // seconds — TTL for chunked upload sessions +}); +``` + +standalone 모드(프로젝트 없이 `os start`)에서는 런타임이 +`.objectstack/data/uploads/` 아래에 로컬 스토리지를 자동으로 구성합니다. +루트 디렉터리는 `OS_STORAGE_ROOT` 환경 변수로 재정의할 수 있습니다. + +**프로덕션 적합성은 배포 형태에 따라 달라집니다:** + +- ✅ 데스크톱 앱, 단일 노드 내부 도구, 엣지 / 온프레미스 + 어플라이언스 — `uploads/` 디렉터리가 파일 시스템 백업에 포함되어 있는 한 + (또는 데스크톱 앱의 경우 사용자가 관리하는 동기화 폴더에 위치하는 한) + 로컬 스토리지로 충분합니다. +- ❌ 멀티 노드, 멀티 AZ, 또는 리전 간 내구성이 필요한 경우 — + S3 호환 스토리지를 사용하세요. + +## S3 호환 (프로덕션) + +적합한 용도: 프로덕션, 멀티 노드, 내구성 + 수명 주기 관리. + +```bash +pnpm add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner +``` + +```ts +import { StorageServicePlugin } from '@objectstack/service-storage'; + +new StorageServicePlugin({ + adapter: 's3', + s3: { + bucket: 'my-bucket', + region: 'us-east-1', + // omit credentials to use the AWS SDK's default chain + // (env, ~/.aws, IAM role) + }, +}); +``` + +AWS SDK는 일반적인 체인에서 자격 증명을 읽습니다: + +| 소스 | 환경 변수 | +|---|---| +| 표준 환경 변수 | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION` | +| 세션 토큰 | `AWS_SESSION_TOKEN` | +| 공유 구성 | `~/.aws/credentials`, `AWS_PROFILE` | +| IAM 역할 | EC2 / ECS / EKS / Lambda에서 자동 — 구성 불필요 | + +### Cloudflare R2 + +```ts +new StorageServicePlugin({ + adapter: 's3', + s3: { + bucket: 'my-bucket', + region: 'auto', + endpoint: 'https://.r2.cloudflarestorage.com', + forcePathStyle: false, + }, +}); +``` + +자격 증명: R2 액세스 키 ID + 시크릿으로, 표준 `AWS_*` +환경 변수나 시크릿 관리자를 통해 전달합니다. + +### MinIO (자체 호스팅) + +```ts +new StorageServicePlugin({ + adapter: 's3', + s3: { + bucket: 'my-bucket', + region: 'us-east-1', + endpoint: 'http://minio.internal:9000', + forcePathStyle: true, + }, +}); +``` + +### DigitalOcean Spaces + +```ts +new StorageServicePlugin({ + adapter: 's3', + s3: { + bucket: 'my-bucket', + region: 'nyc3', + endpoint: 'https://nyc3.digitaloceanspaces.com', + forcePathStyle: false, + }, +}); +``` + +### S3 버킷 정책 + +스토리지는 presigned PUT/GET URL을 사용합니다. 권장 버킷 정책: + +- 모든 퍼블릭 액세스 차단. +- CORS: ObjectOS 호스트명에서의 `PUT`/`GET` 허용. +- 수명 주기: 미완료 멀티파트 업로드는 1~7일 후 만료; `temp=true` 태그가 + 지정된 객체는 24시간 후 만료. +- 버전 관리 + Object Lock: 선택 사항이며, 규정 준수 배포에 권장됩니다. + +## REST 인터페이스 + +`@objectstack/client`가 이를 호출하므로 — 일반적으로 직접 호출할 필요는 없습니다: + +| 메서드 | 경로 | 용도 | +|---|---|---| +| POST | `/api/v1/storage/upload/presigned` | presigned 업로드 URL 가져오기 | +| POST | `/api/v1/storage/upload/complete` | 완료된 업로드 커밋 | +| POST | `/api/v1/storage/upload/chunked` | 청크 업로드 시작 | +| PUT | `/api/v1/storage/upload/chunked/:uploadId/chunk/:i` | 청크 업로드 | +| POST | `/api/v1/storage/upload/chunked/:uploadId/complete` | 청크 업로드 완료 | +| GET | `/api/v1/storage/upload/chunked/:uploadId/progress` | 진행 상황 폴링 | +| GET | `/api/v1/storage/files/:fileId/url` | presigned 다운로드 URL 가져오기 | + +파일별 권한 부여는 보안 플러그인의 권한 평가기가 `sys_file` 객체를 대상으로 +처리하므로 — 별도의 스토리지 계층 ACL이 필요하지 않습니다. + +## 실시간 구성 + +설정 서비스가 활성화되어 있으면(기본적으로 활성화됨), 관리자는 +재시작 없이 **Console → Configuration → Storage**에서 스토리지 어댑터를 +교체할 수 있습니다: + +- 어댑터, 버킷, 리전, 엔드포인트 선택; +- 자격 증명 붙여넣기(`sys_setting`에 저장 시 암호화됨); +- 저장하기 전에 **Test connection** 클릭. + +변경 사항은 다음 요청부터 적용됩니다 — 재시작이 필요 없습니다. + +## 사이징 + +| 리소스 | 기본값 | 조정 가능 여부 | +|---|---|---| +| Presigned URL TTL | 1시간 | `presignedTtl` 플러그인 옵션 | +| 청크 업로드 세션 TTL | 24시간 | `sessionTtl` 플러그인 옵션 | +| 최대 단일 파트 업로드 | 백엔드 제한 (S3 = 5 GB) | — | +| 최대 청크 업로드 | 백엔드 제한 (S3 = 5 TB) | — | + +## 다음 단계 + +- [System settings](/docs/configure/system-settings) — 실시간 + 구성 및 스토리지 교체에 사용되는 설정 서비스 +- [Production Readiness](/docs/operate/production) — 객체 스토리지 + 내구성 및 백업을 포함하는 체크리스트 +- [`@objectstack/service-storage` on GitHub](https://github.com/objectstack-ai/framework/tree/main/packages/services/service-storage) — 소스 및 전체 옵션 참조 diff --git a/content/docs/configure/system-settings.ko.mdx b/content/docs/configure/system-settings.ko.mdx new file mode 100644 index 0000000..a7b9d64 --- /dev/null +++ b/content/docs/configure/system-settings.ko.mdx @@ -0,0 +1,87 @@ +--- +title: 시스템 설정 +description: 매니페스트와 공유 K/V 스토어를 통해 테넌트 및 사용자 설정을 구성합니다. +--- + +# 시스템 설정 + +ObjectStack에는 런타임 및 플러그인 설정을 위한 설정 서비스가 포함되어 +있습니다. ObjectOS는 애플리케이션 아티팩트가 설정 기능을 요구할 때 +Console을 통해 이러한 설정을 노출합니다. + +## 설정 모델 + +설정은 코드로 선언되며 값으로 저장됩니다. + +| 계층 | 목적 | +|---|---| +| 설정 매니페스트 | 필드, 레이블, 기본값, 검증, 가시성, 액션을 정의 | +| `sys_setting` | 테넌트/사용자 K/V 값을 저장 | +| 리졸버 | 유효 값을 읽음 | +| Console UI | 표준 설정 페이지를 렌더링 | + +플러그인은 일반적인 구성을 위해 일회성 설정 테이블을 만들어서는 안 +됩니다. 설정 매니페스트를 선언하고 ObjectOS가 값을 일관되게 +렌더링하고 저장하도록 해야 합니다. + +## 해석 순서 + +각 설정은 범위(`global`, `tenant`, 또는 `user`)를 선언합니다. +리졸버는 우선순위가 가장 높은 것부터 낮은 것까지 캐스케이드를 +따라가며, 설정된 첫 번째 값이 우선합니다. + +```text +Environment override (locked) +Global setting +Tenant setting +User setting +Manifest default +``` + +환경 재정의는 잠겨 있습니다. 값이 환경 변수를 통해 설정되면 Console +UI는 이를 호스트에 의해 관리되는 것으로 표시하고 런타임 편집을 +거부합니다. 체인 상단의 잠긴 값은 유효 값도 잠그므로, 하위 범위가 +이를 가릴 수 없습니다. + +## 일반적인 설정 영역 + +고객 대상 설정에는 일반적으로 다음이 포함됩니다. + +| 영역 | 예시 | +|---|---| +| Email | SMTP 호스트, 제공업체 API 키, 발신 주소, 테스트 이메일 | +| Branding | 제품명, 로고 URL, 강조 색상, 기본 테마 | +| Feature flags | 테넌트 수준 기능 스위치 | +| Storage | 로컬 파일 시스템 또는 객체 스토리지 자격 증명 | +| AI providers | 모델 제공업체, API 키, 예산 한도 | +| SSO | OIDC 제공업체 설정 및 연결 테스트 | + +## 시크릿 + +비밀번호 및 자격 증명 필드는 설정 서비스에 의해 암호화되거나 환경 +관리 값으로 제공되어야 합니다. 시크릿 제공자가 구성되면 설정 +서비스는 암호문을 `sys_secret`에 저장하고 `sys_setting`에는 핸들만 +보관합니다. 시크릿을 아티팩트, compose 파일 또는 Git에 저장하지 +마십시오. + +## 테스트 액션 + +설정 매니페스트는 다음과 같은 표준 액션 버튼을 선언할 수 있습니다. + +- 테스트 이메일 전송; +- SSO 디스커버리 테스트; +- 객체 스토리지 자격 증명 확인; +- 웹훅 대상 검증. + +운영자에게 먼저 로그를 검사하도록 요청하는 대신, 고객 설정 +플로우에 이러한 액션을 사용하십시오. + +## 관련 항목 + +여러 설정 영역에는 전용 구성 가이드가 있습니다. + +- [Email](/docs/configure/email) +- [Storage](/docs/configure/storage) +- [AI providers](/docs/configure/ai) +- [Authentication and SSO](/docs/configure/authentication) +- [Runtime](/docs/configure/runtime) diff --git a/content/docs/configure/webhooks.ko.mdx b/content/docs/configure/webhooks.ko.mdx new file mode 100644 index 0000000..767b0a9 --- /dev/null +++ b/content/docs/configure/webhooks.ko.mdx @@ -0,0 +1,99 @@ +--- +title: 웹훅 +description: 아웃바운드 웹훅 전달, 서명, 재시도. +--- + +# 웹훅 + +ObjectOS는 아웃바운드 웹훅을 위해 영구적인 **outbox** 모델을 사용합니다. +웹훅 플러그인이 활성화되면 비즈니스 변경 사항이 전달 행을 큐에 추가하고, +백그라운드 디스패처가 재시도와 함께 이를 전달합니다 — 따라서 느리거나 +응답하지 않는 수신자가 원래의 트랜잭션을 절대 차단하지 않습니다. + +## 웹훅 활성화 + +웹훅은 선택적 기능입니다. 배포된 ObjectOS 이미지에는 +`@objectstack/plugin-webhooks`가 포함되어야 하며, 애플리케이션 아티팩트는 +웹훅 구독을 등록해야 합니다(일반적으로 `sys_webhook` 객체의 레코드 형태). + +활성화되면 Console에 두 개의 객체가 나타납니다: + +| 객체 | 용도 | +|---|---| +| `sys_webhook` | 웹훅 구독(대상 URL, 이벤트 필터, 시크릿, 상태) | +| `sys_webhook_delivery` | 전달 로그(URL, 응답 코드, 시도 횟수, 재시도 타임스탬프) | + +## 전달 시맨틱 + +- **최소 한 번(At-least-once).** 일시적 실패 이후 전달이 재시도될 수 있으므로, + 수신자는 멱등성을 보장해야 합니다. +- **영구성(Persistent).** 전달 정보는 비즈니스 데이터베이스에 저장되므로 + ObjectOS 재시작 후에도 유지됩니다. +- **파티셔닝(Partitioned).** 각 디스패처 워커는 outbox의 파티션을 점유하므로, + 배포 환경에서 중복 전달 없이 디스패치를 수평으로 확장할 수 있습니다. +- **제한된 재시도(Bounded retries).** 실패한 전달은 구성 가능한 상한선까지 + 백오프와 함께 재시도되며, 소진된 행은 검사를 위해 `sys_webhook_delivery`에 + 남습니다. + +## 서명 + +모든 전달에는 수신자가 라우팅, 중복 제거, 검증할 수 있도록 식별 헤더가 +포함됩니다: + +```text +X-Objectstack-Event: +X-Objectstack-Delivery: +X-Objectstack-Attempt: +``` + +웹훅 구독에 `secret`이 있는 경우, ObjectOS는 모든 요청에 서명도 추가합니다: + +```text +X-Objectstack-Signature: sha256= +``` + +서명은 원시 요청 본문에 대해 계산된 `HMAC-SHA256(secret, body)`입니다. +페이로드를 신뢰하기 전에 수신자에서 이를 검증하십시오: + +```js +import { createHmac, timingSafeEqual } from 'node:crypto'; + +function verify(body, signatureHeader, secret) { + const expected = 'sha256=' + createHmac('sha256', secret).update(body).digest('hex'); + return timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected)); +} +``` + +시크릿을 교체하려면 새 시크릿으로 새 구독을 발급하고, 전환 기간 동안 두 +구독을 함께 실행한 다음, 이전 구독을 비활성화하십시오. + +## 수신자 기대 사항 + +- 몇 초 이내에 `2xx`로 응답하십시오. `408`, `429`, 또는 `5xx` 응답(또는 + 타임아웃/전송 오류)은 재시도 가능하며 백오프와 함께 재시도됩니다. 그 외의 + `4xx`는 영구적 실패로 처리되어 추가 재시도 없이 `dead` 상태로 이동됩니다. +- 디스패처는 `2xx`를 확인하기 전까지 전달을 성공으로 표시하지 **않으므로**, + 실패한 수신자의 행은 검사나 재전달을 위해 `sys_webhook_delivery`에 + 유지됩니다. +- 멱등성을 보장하십시오 — `X-Objectstack-Delivery` 헤더(전달 id) 또는 + 페이로드 내 자체 이벤트 id를 기준으로 중복을 제거하십시오. + +## 실패 처리 + +문제가 발생하면: + +1. 해당 행에 대해 `sys_webhook_delivery`를 확인하십시오 — `status`, + `response_code`, `response_body`, `attempts`가 기록됩니다. +2. ObjectOS에서 수신자로의 아웃바운드 네트워크 액세스를 확인하십시오. +3. 수신자가 영구적으로 변경된 경우, 구독 URL을 업데이트하고 Console에서 해당 + 행을 재전달하십시오. +4. 사고 검토 시, 감사 로그(`sys_audit_log`)는 구독 편집 사항을 기록하지만 + 페이로드는 기록하지 않습니다 — 페이로드는 outbox에 남습니다. + +## 운영 팁 + +- 웹훅 URL에 시크릿을 넣지 마십시오(쿼리 문자열은 로그에 기록됩니다). +- 전용 수신자 호스트네임을 사용하면 메인 앱에 영향을 주지 않고 엣지에서 + 차단하여 부하를 분산할 수 있습니다. +- 디스패처 지연을 모니터링하십시오 — outbox가 계속 증가하는 것은 보통 + 수신자가 성능 저하 상태임을 의미합니다. diff --git a/content/docs/deploy/air-gapped.ko.mdx b/content/docs/deploy/air-gapped.ko.mdx new file mode 100644 index 0000000..5e2297c --- /dev/null +++ b/content/docs/deploy/air-gapped.ko.mdx @@ -0,0 +1,69 @@ +--- +title: 에어갭(Air-gapped) 배포 +description: 공용 인터넷 접근 없이 ObjectOS를 실행합니다. +--- + +# 에어갭(Air-gapped) 배포 + +에어갭 배포는 ObjectOS가 런타임에 호스팅된 컨트롤 플레인이나 공용 패키지 +레지스트리를 호출할 수 없는 고객 네트워크를 위한 것입니다. + +## 무엇을 제공해야 하나 + +오프라인 릴리스 번들에는 다음 항목이 포함되어야 합니다. + +| 항목 | 목적 | +|---|---| +| ObjectOS 컨테이너 이미지 | 런타임 바이너리 및 종속성 | +| `objectstack.json` 아티팩트 | 변경 불가능한 애플리케이션 정의 | +| 데이터베이스 마이그레이션/초기화 가이드 | 고객 비즈니스 데이터베이스 설정 | +| 환경 템플릿 | 필요한 시크릿 및 런타임 변수 | +| 운영 체크리스트 | 업그레이드, 롤백, 백업 및 진단 | + +컨테이너 이미지와 아티팩트 마운트는 표준 [Docker 배포](/docs/deploy/docker)와 +동일한 규칙을 따르며, 차이점은 네트워크 구성뿐입니다. + +## 런타임 모드 + +파일 기반 모드를 사용하세요. ObjectOS가 로컬 아티팩트와 고객이 관리하는 +비즈니스 데이터베이스를 가리키도록 하고, 클라우드 컨트롤 플레인은 설정하지 +않은 채로 둡니다. + +```bash +OS_ARTIFACT_FILE=/artifacts/objectstack.json +OS_BUSINESS_DB_URL=file:/var/lib/objectos/data.db +# Leave OS_CLOUD_URL unset to run fully offline +``` + +ObjectOS는 모든 요청을 패키징된 프로젝트로 확인하고 디스크에서 아티팩트를 +로드합니다. `OS_CLOUD_URL`을 설정하지 않으면 호스팅된 컨트롤 플레인으로의 +호출이 발생하지 않습니다. + +## 네트워크 요건 + +이 모드에서 ObjectOS는 공용 인터넷 접근이 필요하지 않습니다. 고객 네트워크 +규칙은 다음만 허용해야 합니다. + +- 승인된 인그레스 또는 로드 밸런서로부터의 인바운드 HTTP/HTTPS; +- 고객이 관리하는 비즈니스 데이터베이스로의 아웃바운드 데이터베이스 트래픽; +- SMTP, 오브젝트 스토리지, 웹훅 대상 또는 ID 공급자와 같이 명시적으로 구성된 + 통합으로의 아웃바운드 트래픽. + +## 인증 + +고객이 OIDC/SSO를 사용하는 경우, ID 공급자가 에어갭 네트워크에서 접근 +가능해야 합니다. 그렇지 않은 경우, 로컬 이메일/비밀번호 인증 또는 동일한 +네트워크 내부에 호스팅된 ID 공급자를 사용하세요. + +## 업그레이드 프로세스 + +아티팩트는 변경 불가능한 것으로 취급하세요. + +1. 새 ObjectOS 이미지를 가져옵니다. +2. 새 아티팩트를 이전 아티팩트 옆에 배치합니다. +3. 마운트 또는 환경 변수를 새 아티팩트를 가리키도록 업데이트합니다. +4. ObjectOS를 재시작합니다. +5. 이전 이미지 태그 또는 아티팩트 경로를 복원하여 롤백합니다. + +이 단계들이 요약하는 전체 절차는 [업그레이드](/docs/operate/upgrade) 및 +[백업](/docs/operate/backup)을 참조하세요. diff --git a/content/docs/deploy/docker.ko.mdx b/content/docs/deploy/docker.ko.mdx new file mode 100644 index 0000000..9783fdf --- /dev/null +++ b/content/docs/deploy/docker.ko.mdx @@ -0,0 +1,151 @@ +--- +title: Docker +description: 평가, 스테이징 또는 프로덕션을 위해 ObjectOS를 컨테이너에서 실행합니다. +--- + +# Docker + +Docker는 대부분의 팀이 프로덕션으로 가는 경로입니다. 이는 런타임이 +운영되도록 의도된 방식과 일치합니다. 즉, 인스턴스당 하나의 무상태 +컨테이너, 외부 데이터베이스, 파일을 위한 외부 객체 스토리지, +런타임에 주입되는 시크릿입니다. + +## 사전 빌드된 이미지 가져오기 + +`main`으로 푸시할 때마다 그리고 태그가 지정된 릴리스마다 런타임 +이미지를 GitHub Container Registry에 게시합니다. + +```bash +docker pull ghcr.io/objectstack-ai/objectos:latest +``` + +사용 가능한 태그: + +| Tag | 채널 | +|---|---| +| `latest` | `main`의 최신 빌드 (최초 평가에 권장) | +| `main` | `latest`와 동일하지만 명시적 | +| `sha-` | 특정 커밋에 고정 (프로덕션에 권장) | +| `vX.Y.Z`, `X.Y`, `X` | 태그가 지정된 릴리스 | + +프로덕션의 경우 `sha-` 또는 semver 태그에 고정하세요. `latest`는 +변동됩니다. + +## 번들된 샘플로 실행 + +```bash +docker run --rm -p 3000:3000 \ + -e OS_AUTH_SECRET="$(openssl rand -hex 32)" \ + ghcr.io/objectstack-ai/objectos:latest +``` + +그런 다음 http://localhost:3000 을 엽니다. 이미지에는 빈 샘플 앱이 +함께 제공되므로, 자신의 아티팩트를 가리키기 전에 런타임이 부팅되고 +Console/Account가 렌더링되는지 확인할 수 있습니다. + +`OS_AUTH_SECRET`은 시작에 필요합니다. 이를 교체하면 기존 세션이 +무효화됩니다. + +## 자신의 아티팩트로 실행 + +앱 정의는 단일 파일입니다: `dist/objectstack.json`. 이는 다음으로 +생성됩니다: + +| 소스 | 방법 | +|---|---| +| 자신의 프로젝트 | `objectstack.config.ts`를 편집한 후 `pnpm exec objectstack compile` | +| 템플릿 | [templates repo](https://github.com/objectstack-ai/templates)에서 복제하고 `pnpm install && pnpm exec objectstack compile` 실행 | +| 앱 marketplace | 게시된 앱의 일부로 제공됨 | + +파일을 `/artifacts/objectstack.json`에 마운트하고 런타임이 이를 +가리키도록 합니다. + +```bash +docker run --rm -p 3000:3000 \ + -e OS_AUTH_SECRET="$(openssl rand -hex 32)" \ + -e OS_ARTIFACT_FILE=/artifacts/objectstack.json \ + -v "$PWD/dist:/artifacts:ro" \ + -v objectos-data:/var/lib/objectos \ + ghcr.io/objectstack-ai/objectos:latest +``` + +`objectos-data` 볼륨은 (외부 DB가 구성되지 않은 경우) SQLite +데이터베이스와 로컬에 저장된 업로드 파일을 보관합니다. + +## SQLite 대신 Postgres 사용 + +```bash +docker run --rm -p 3000:3000 \ + -e OS_AUTH_SECRET="$(openssl rand -hex 32)" \ + -e OS_ARTIFACT_FILE=/artifacts/objectstack.json \ + -e OS_DATABASE_DRIVER=postgres \ + -e OS_DATABASE_URL='postgres://user:pass@db.internal:5432/myapp' \ + -v "$PWD/dist:/artifacts:ro" \ + ghcr.io/objectstack-ai/objectos:latest +``` + +드라이버와 연결 옵션의 전체 매트릭스는 [Runtime Configuration](/docs/configure/runtime)을 +참조하세요. + +## Docker Compose + +참조용 `docker-compose.yml`은 저장소의 `docker/` 아래에 있습니다. + +```bash +git clone https://github.com/objectstack-ai/objectos.git +cd objectos +mkdir -p docker/artifacts +cp /path/to/your/objectstack.json docker/artifacts/ +export OS_AUTH_SECRET="$(openssl rand -hex 32)" +docker compose -f docker/docker-compose.yml up +``` + +기본 포트는 `3000`입니다. `OBJECTOS_PORT=3200 docker compose ...`로 재정의하세요. + +## 자신의 이미지 빌드 + +아티팩트, 사용자 정의 플러그인 또는 자신의 SSL/CA 번들을 포함해야 +하는 경우: + +```bash +git clone https://github.com/objectstack-ai/objectos.git +cd objectos +docker build -f docker/Dockerfile -t myorg/objectos:custom . +``` + +함께 제공되는 Dockerfile은 Node 20 Alpine에서 슬림한 런타임 이미지를 +생성하는 다단계 빌드입니다. + +## 분리해서 보관해야 하는 것 + +| 자산 | 위치 | +|---|---| +| 아티팩트 (`objectstack.json`) | 이미지(불변) 또는 마운트된 볼륨 | +| 비즈니스 데이터베이스 | 외부에서 관리되는 Postgres / MySQL / Mongo | +| ID 및 세션 | 동일한 데이터베이스 | +| 업로드된 파일 | 외부에서 관리되는 S3 / R2 / 디스크 볼륨 | +| 시크릿 (`OS_AUTH_SECRET`, DB URL, API 키) | 시크릿 매니저, 환경 변수로 주입 | +| 로그 | stdout — 로그 드라이버가 수집 | + +## 확인 + +시작 후: + +```bash +curl -fsS http://localhost:3000/health +# {"status":"ok",...} +``` + +그런 다음 http://localhost:3000/_account/register 에서 로그인하고 +API가 응답하는지 확인합니다. + +```bash +curl http://localhost:3000/api/v1/data/ +``` + +## 다음 + +- [Kubernetes](/docs/deploy/kubernetes) — 프로덕션급 오케스트레이션 +- [Air-gapped](/docs/deploy/air-gapped) — 인터넷 송신 없이 실행 +- [Production Readiness](/docs/operate/production) — 사전 점검 체크리스트 +- [Observability](/docs/operate/observability) — 로그, 메트릭, 감사 diff --git a/content/docs/deploy/index.ko.mdx b/content/docs/deploy/index.ko.mdx new file mode 100644 index 0000000..c90a215 --- /dev/null +++ b/content/docs/deploy/index.ko.mdx @@ -0,0 +1,24 @@ +--- +title: 배포 +description: ObjectOS를 실행하는 방식을 선택하세요 — 평가용 Docker, 프로덕션 HA용 Kubernetes, 또는 외부 통신이 없는 네트워크를 위한 에어갭 번들. +--- + +ObjectOS는 모든 환경에서 동일하게 실행되는 단일 런타임 아티팩트로 제공됩니다. 인프라에 맞는 배포 형태를 선택하세요. + +## Docker + +단일 컨테이너는 ObjectOS를 평가하거나 단일 호스트에서 실행하는 가장 빠른 방법입니다. 데이터 디렉터리용 볼륨을 마운트하고, 데이터베이스를 지정한 다음, 컨테이너를 시작하세요. + +[Docker 가이드 →](/docs/deploy/docker) + +## Kubernetes + +프로덕션 환경에서는 ObjectOS를 Service 뒤의 Deployment로 실행하며, 데이터 디렉터리에 영구 스토리지를 사용하고 시크릿은 표준 Kubernetes 메커니즘을 통해 제공합니다. + +[Kubernetes 가이드 →](/docs/deploy/kubernetes) + +## Air-Gapped + +인터넷 외부 통신이 없는 네트워크로 릴리스 번들을 반입하세요. ObjectOS는 로컬 파일에서 애플리케이션 아티팩트를 읽으며 호스팅된 서비스에 절대 접근하지 않습니다. + +[에어갭 가이드 →](/docs/deploy/air-gapped) diff --git a/content/docs/deploy/kubernetes.ko.mdx b/content/docs/deploy/kubernetes.ko.mdx new file mode 100644 index 0000000..b4ab08b --- /dev/null +++ b/content/docs/deploy/kubernetes.ko.mdx @@ -0,0 +1,106 @@ +--- +title: Kubernetes +description: Kubernetes 환경에서 ObjectOS를 배포합니다. +--- + +# Kubernetes + +관리형 시크릿, 인그레스, 프로브, 롤링 업그레이드, 고객 관리형 데이터베이스가 +필요한 프로덕션 배포에는 Kubernetes를 사용하세요. + +ObjectOS는 아직 패키징된 Helm 차트를 제공하지 않으므로, 아래 가이드는 +게시된 컨테이너 이미지를 중심으로 구성할 매니페스트를 설명합니다. + +## 배포 구성 + +프로덕션 ObjectOS 배포에는 일반적으로 다음이 포함됩니다. + +| 구성 요소 | 권장 사항 | +|---|---| +| Deployment | 단일 ObjectOS 컨테이너 이미지 | +| Service | HTTP 트래픽을 위한 ClusterIP 서비스 | +| Ingress | TLS 종료 및 고객 호스트명 라우팅 | +| Secret | `OS_AUTH_SECRET`, 컨트롤 플레인 토큰, 데이터베이스 자격 증명 | +| ConfigMap | 시크릿이 아닌 런타임 구성 | +| Persistent storage | 로컬 파일 아티팩트나 SQLite 평가 데이터를 사용할 때만 필요 | +| External database | 프로덕션 비즈니스 데이터에 권장 | + +## 필수 구성 + +최소한 다음을 구성하세요. + +```yaml +env: + - name: PORT + value: "3000" + - name: OS_AUTH_SECRET + valueFrom: + secretKeyRef: + name: objectos-secrets + key: auth-secret +``` + +클라우드 연결 모드의 경우: + +```yaml +env: + - name: OS_CLOUD_URL + value: "https://cloud.example.com" + - name: OS_CLOUD_API_KEY + valueFrom: + secretKeyRef: + name: objectos-secrets + key: cloud-api-key +``` + +파일 기반 모드의 경우, 아티팩트를 마운트하고 다음을 설정하세요. + +```yaml +env: + - name: OS_ARTIFACT_FILE + value: "/artifacts/objectstack.json" +``` + +## 프로브 + +ObjectOS는 프로젝트 커널이 완전히 해석되기 전에 응답하는 내장 `GET /health` +엔드포인트를 제공하며, 이는 liveness 및 readiness 프로브 모두에 적합한 +대상입니다. + +```yaml +readinessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 10 +livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 15 + periodSeconds: 20 +``` + +더 엄격한 readiness 검사를 원한다면, 추가 프로브를 애플리케이션 고유의 +생성된 API 라우트로 지정하세요. 이는 아티팩트가 로드되고 커널이 요청을 +처리하고 있음을 확인해 줍니다. + +## 인그레스와 CORS + +엣지 또는 인그레스에서 TLS를 종료하세요. 고객이 사용하는 프론트엔드 +오리진에 대해 CORS를 명시적으로 구성하세요. 와일드카드 오리진과 자격 증명을 +포함한 요청을 함께 사용하지 마세요. + +ObjectOS가 프록시 뒤에서 실행될 때는, 프록시가 자체 값을 설정하기 전에 +클라이언트가 제공한 `X-Forwarded-For` 값을 제거하는지 확인하세요. 속도 +제한과 감사 추적은 신뢰할 수 있는 호출자 신원에 의존합니다. + +## 롤링 업그레이드 + +ObjectOS 이미지 버전과 애플리케이션 아티팩트 버전은 분리되어 있습니다. +각각 독립적으로 롤링하세요. + +- ObjectOS 이미지: 컨테이너 태그를 변경하여 업그레이드합니다. +- 애플리케이션 아티팩트: 새로운 불변 아티팩트를 게시하거나 마운트합니다. +- 롤백: 이전 이미지 태그 또는 아티팩트 포인터를 복원합니다. diff --git a/content/docs/deploy/meta.ko.json b/content/docs/deploy/meta.ko.json new file mode 100644 index 0000000..d3f3cc1 --- /dev/null +++ b/content/docs/deploy/meta.ko.json @@ -0,0 +1,10 @@ +{ + "title": "배포", + "defaultOpen": false, + "pages": [ + "index", + "docker", + "kubernetes", + "air-gapped" + ] +} diff --git a/content/docs/extend-existing-systems.de.mdx b/content/docs/extend-existing-systems.de.mdx new file mode 100644 index 0000000..331f060 --- /dev/null +++ b/content/docs/extend-existing-systems.de.mdx @@ -0,0 +1,107 @@ +--- +title: Bestehende Systeme erweitern +description: Verbinde ObjectOS mit den Geschäftssystemen, die du bereits betreibst, und ergänze KI-native Abfrage, Analyse und Automatisierung — ohne Migration. +--- + +# Bestehende Systeme erweitern + +Die meisten Teams, die ObjectOS evaluieren, haben bereits ein System of +Record — ein CRM, ein ERP, ein Ticketing-Tool, ein selbstgebautes Back +Office, das auf einer produktiven SQL- oder MongoDB-Datenbank läuft. Die +Frage lautet selten „Sollten wir es wegwerfen und neu bauen?". Sie +lautet „Können wir das, was wir bereits haben, **KI-nativ** machen, +ohne eine riskante Migration?" + +Genau diesen Weg beschreibt diese Seite: **Verbinde ObjectOS mit deiner +bestehenden Datenbank, modelliere die Tabellen, die dir wichtig sind, +als Objekte und lass KI-Agenten diese Daten abfragen, analysieren und +darauf handeln** — unter deinen Berechtigungen, auf deiner +Infrastruktur, ohne dass das Originalsystem angetastet wird. + +## Die Form des Schritts + +Du ersetzt dein Geschäftssystem nicht. Du stellst ObjectOS *daneben* und +richtest es auf dieselbe Datenbank: + +1. **Verbinde** die bestehende Datenbank als [Datasource](/docs/configure/data-sources). + Die Anmeldedaten kommen aus deiner Umgebung; die Verbindung kann + read-only sein, wenn du nur analysieren möchtest. +2. **Modelliere** die Tabellen als Objekte — von Hand oder indem du einen + Coding-Agenten das Schema scannen und quellbasierte Objektdateien für + dich generieren lässt. +3. **Binde** jedes Objekt an die Datasource (pro Objekt oder mit einer + Routing-Regel für einen ganzen Namespace). +4. **Nutze KI** — sobald eine Tabelle ein Objekt ist, arbeitet jeder + Agent, jedes Tool, jeder Flow und jedes Dashboard damit, automatisch + an die richtige Datenbank geroutet. + +An der Legacy-Anwendung ändert sich nichts. Die Datensätze bleiben, wo +sie sind. ObjectOS wird die KI-native, berechtigungsbewusste +Oberfläche obendrauf. + +## Warum das ohne Neuschreiben funktioniert + +| Bedenken | Wie ObjectOS damit umgeht | +|---|---| +| „Wir können die Daten nicht verschieben" | Die Daten werden nie verschoben. ObjectOS verbindet sich mit deiner Datenbank an Ort und Stelle. | +| „Wir können keine Schreibzugriffe auf die Produktion riskieren" | Binde Objekte an eine **read-only**-Datasource (oder einen read-only DB-Benutzer). Analysiere zuerst sicher; aktiviere Schreibzugriffe gezielt. | +| „Jede Tabelle zu modellieren ist wochenlange Arbeit" | Ein Coding-Agent scannt das Schema und generiert eine Objektdatei pro Tabelle — du prüfst und verfeinerst, du tippst nichts von Hand. | +| „Man kann KI nicht mit unseren Daten vertrauen" | Agenten laufen als der **angemeldete Benutzer** und gehorchen den Berechtigungen auf Objekt-, Datensatz- und Feldebene. Sie sehen nie mehr als die Person hinter ihnen. | +| „Unsere Daten dürfen unser Netzwerk nicht verlassen" | ObjectOS läuft in deiner Umgebung. Geschäftsdaten und Prompts bleiben innerhalb deines Perimeters. | + +## Objekte mit einem Coding-Agenten generieren + +Der schnellste Weg, ein bestehendes Schema einzubinden, ist die +Verwendung eines Coding-Agenten (wie Claude Code), um die +**Geschäftstabellen zu scannen und quellbasierte Objektdefinitionen zu +generieren** — eine `*.object.ts`-Datei pro Tabelle. + +Die Referenz-App [`hotcrm`](https://github.com/objectstack-ai/hotcrm) +zeigt die genaue Form, die diese Ausgabe annehmen sollte: Jede Tabelle +wird zu einer `src/objects/.object.ts` mit +`ObjectSchema.create({ … })` mit typisierten `Field.*`-Definitionen und +`Field.lookup(...)` für Fremdschlüssel, zusammengesetzt durch +`defineStack`. Der Agent introspiziert deine verbundene Datenbank, +mappt Spalten auf Feldtypen und schreibt Objekte, die dir gehören und +die du commitest. Du behältst, was passt, lässt Spalten weg, die du +nicht freigeben willst, und ergänzt obendrauf Labels, Validierungen und +Berechtigungen. + +Siehe [Data Sources](/docs/configure/data-sources) für den +vollständigen, schrittweisen Authoring-Leitfaden. + +## Was du am ersten Tag bekommst + +Sobald die Tabellen als Objekte modelliert und an deine bestehende +Datenbank gebunden sind: + +- **Natürlichsprachliche Analyse.** Nutzer stellen Fragen zu den echten + Datensätzen — „welche Deals sind dieses Quartal abgerutscht und wem + gehören sie?" — und die Antwort wird über ObjectQL gegen Live-Daten + berechnet. +- **Gesteuerte Automatisierung.** Flows und Aktionen können dieselben + Daten lesen und (wo erlaubt) schreiben, wobei jeder Schritt auditiert + wird. +- **Eine generierte API und Console.** REST-/GraphQL-Endpunkte und + Admin-Bildschirme stammen aus denselben Metadaten — keine zusätzliche + Integrationsschicht. +- **Ein einziges Berechtigungsmodell.** Die Grenze, die für Menschen + gilt, gilt identisch für KI-Traffic. + +## Wohin das führt + +Der obige Ablauf funktioniert heute mit ausgelieferten Bausteinen. Ein +reichhaltigeres, **schlüsselfertiges Federations**-Erlebnis — +einstufiger Schema-Import, Bindung extern verwalteter Schemas und +eingebaute Sicherheits-Gates — ist in aktiver Konzeption unter +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(Status: *Proposed*). Bis es soweit ist, ist der dokumentierte Weg — +verbinden, modellieren, binden, abfragen — die unterstützte Art, ein +bestehendes System zu erweitern. + +## Hier starten + +- [Data Sources](/docs/configure/data-sources) — eine Datenbank verbinden, Objekte binden, Abfragen routen +- [AI Agents](/docs/build/agents) — deklarative Agenten über deinen Objekten +- [Permissions](/docs/configure/permissions) — das Modell, das KI erbt +- [Quickstart](/docs/quickstart) — eine Runtime in Minuten aufsetzen diff --git a/content/docs/extend-existing-systems.es.mdx b/content/docs/extend-existing-systems.es.mdx new file mode 100644 index 0000000..26eca6d --- /dev/null +++ b/content/docs/extend-existing-systems.es.mdx @@ -0,0 +1,106 @@ +--- +title: Extiende sistemas existentes +description: Conecta ObjectOS a los sistemas de negocio que ya operas, y luego añade consulta, análisis y automatización nativos de IA — sin una migración. +--- + +# Extiende sistemas existentes + +La mayoría de los equipos que evalúan ObjectOS ya tienen un sistema de +registro — un CRM, un ERP, una herramienta de tickets, un back office +hecho en casa sobre una base de datos SQL o MongoDB de producción. La +pregunta rara vez es "¿deberíamos tirarlo y reconstruirlo?". Es "¿podemos +hacer que lo que ya tenemos sea **nativo de IA**, sin una migración +arriesgada?" + +Ese es exactamente el camino que describe esta página: **conecta ObjectOS +a tu base de datos existente, modela como objetos las tablas que te +importan, y deja que los agentes de IA consulten, analicen y actúen sobre +esos datos** — bajo tus permisos, en tu infraestructura, con el sistema +original intacto. + +## La forma de la jugada + +No reemplazas tu sistema de negocio. Pones ObjectOS *al lado* de él y lo +apuntas a la misma base de datos: + +1. **Conecta** la base de datos existente como un [datasource](/docs/configure/data-sources). + Las credenciales provienen de tu entorno; la conexión puede ser de + solo lectura si solo quieres analizar. +2. **Modela** las tablas como objetos — a mano, o dejando que un agente + de codificación escanee el esquema y genere por ti archivos de objetos + a nivel de código fuente. +3. **Vincula** cada objeto al datasource (por objeto, o con una regla de + enrutamiento para todo un namespace). +4. **Usa IA** — en el momento en que una tabla es un objeto, cada agente, + herramienta, flujo y panel funciona sobre ella, enrutado + automáticamente a la base de datos correcta. + +Nada de la aplicación heredada cambia. Las filas permanecen donde están. +ObjectOS se convierte en la superficie nativa de IA y consciente de +permisos por encima. + +## Por qué esto funciona sin una reescritura + +| Preocupación | Cómo lo maneja ObjectOS | +|---|---| +| "No podemos mover los datos" | Los datos nunca se mueven. ObjectOS se conecta a tu base de datos en su lugar. | +| "No podemos arriesgar escrituras en producción" | Vincula los objetos a un datasource de **solo lectura** (o a un usuario de BD de solo lectura). Analiza con seguridad primero; habilita las escrituras deliberadamente. | +| "Modelar cada tabla son semanas de trabajo" | Un agente de codificación escanea el esquema y genera un archivo de objeto por tabla — tú revisas y refinas, no escribes a mano. | +| "No se puede confiar a la IA nuestros datos" | Los agentes se ejecutan como el **usuario que ha iniciado sesión** y obedecen los permisos a nivel de objeto, de registro y de campo. Nunca ven más que la persona detrás de ellos. | +| "Nuestros datos no pueden salir de nuestra red" | ObjectOS se ejecuta en tu entorno. Los datos de negocio y los prompts permanecen dentro de tu perímetro. | + +## Generar objetos con un agente de codificación + +La forma más rápida de incorporar un esquema existente es usar un agente +de codificación (como Claude Code) para **escanear las tablas de negocio +y generar definiciones de objetos a nivel de código fuente** — un archivo +`*.object.ts` por tabla. + +La app de referencia [`hotcrm`](https://github.com/objectstack-ai/hotcrm) +muestra la forma exacta que debería tomar esa salida: cada tabla se +convierte en un `src/objects/.object.ts` usando +`ObjectSchema.create({ … })` con definiciones tipadas de `Field.*` y +`Field.lookup(...)` para las claves foráneas, ensamblado por +`defineStack`. El agente introspecciona tu base de datos conectada, mapea +las columnas a tipos de campo, y escribe objetos de los que eres dueño y +que confirmas. Conservas lo que encaja, descartas las columnas que no +quieres exponer, y añades etiquetas, validaciones y permisos por encima. + +Consulta [Data Sources](/docs/configure/data-sources) para la guía de +autoría completa, paso a paso. + +## Lo que obtienes el primer día + +Una vez que las tablas están modeladas como objetos vinculados a tu base +de datos existente: + +- **Análisis en lenguaje natural.** Los usuarios hacen preguntas sobre los + registros reales — "¿qué tratos se escaparon este trimestre y quién los + gestiona?" — y la respuesta se calcula sobre datos en vivo a través de + ObjectQL. +- **Automatización gobernada.** Los flujos y las acciones pueden leer y + (donde esté permitido) escribir los mismos datos, con cada paso + auditado. +- **Una API y una Console generadas.** Los endpoints REST/GraphQL y las + pantallas de administración provienen de los mismos metadatos — sin una + capa de integración adicional. +- **Un único modelo de permisos.** El límite que se aplica a los humanos + se aplica de forma idéntica al tráfico de IA. + +## Hacia dónde se dirige esto + +El flujo anterior funciona hoy con bloques de construcción ya +disponibles. Una experiencia de **federación llave en mano** más rica — +importación de esquemas en un solo paso, vinculación de esquemas de +propiedad externa, y barreras de seguridad integradas — está en diseño +activo bajo [ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(estado: *Propuesto*). Hasta que llegue, el camino documentado — +conectar, modelar, vincular, consultar — es la forma soportada de +extender un sistema existente. + +## Empieza aquí + +- [Data Sources](/docs/configure/data-sources) — conecta una base de datos, vincula objetos, enruta consultas +- [AI Agents](/docs/build/agents) — agentes declarativos sobre tus objetos +- [Permissions](/docs/configure/permissions) — el modelo que la IA hereda +- [Quickstart](/docs/quickstart) — levanta un runtime en minutos diff --git a/content/docs/extend-existing-systems.fr.mdx b/content/docs/extend-existing-systems.fr.mdx new file mode 100644 index 0000000..2b41329 --- /dev/null +++ b/content/docs/extend-existing-systems.fr.mdx @@ -0,0 +1,107 @@ +--- +title: Étendre les systèmes existants +description: Connectez ObjectOS aux systèmes métier que vous exploitez déjà, puis ajoutez requêtes, analyses et automatisations natives IA — sans migration. +--- + +# Étendre les systèmes existants + +La plupart des équipes qui évaluent ObjectOS disposent déjà d'un système +de référence — un CRM, un ERP, un outil de ticketing, un back-office +maison reposant sur une base de données SQL ou MongoDB en production. La +question n'est presque jamais « devrions-nous tout jeter et reconstruire ? ». +C'est « pouvons-nous rendre **natif IA** ce que nous avons déjà, sans +migration risquée ? » + +C'est exactement le parcours que décrit cette page : **connecter +ObjectOS à votre base de données existante, modéliser comme objets les +tables qui vous intéressent, et laisser les agents IA interroger, +analyser et agir sur ces données** — sous vos permissions, sur votre +infrastructure, le système d'origine restant intact. + +## La forme de la démarche + +Vous ne remplacez pas votre système métier. Vous placez ObjectOS *à +côté* de lui et le pointez vers la même base de données : + +1. **Connectez** la base de données existante comme [datasource](/docs/configure/data-sources). + Les identifiants proviennent de votre environnement ; la connexion + peut être en lecture seule si vous souhaitez uniquement analyser. +2. **Modélisez** les tables comme objets — à la main, ou en laissant un + agent de codage analyser le schéma et générer pour vous des fichiers + d'objets au niveau du code source. +3. **Liez** chaque objet à la datasource (par objet, ou avec une règle de + routage pour tout un espace de noms). +4. **Utilisez l'IA** — dès qu'une table est un objet, chaque agent, + outil, flux et tableau de bord fonctionne dessus, routé + automatiquement vers la bonne base de données. + +Rien ne change dans l'application héritée. Les lignes restent là où elles +sont. ObjectOS devient la surface native IA et consciente des +permissions par-dessus. + +## Pourquoi cela fonctionne sans réécriture + +| Préoccupation | Comment ObjectOS la gère | +|---|---| +| « Nous ne pouvons pas déplacer les données » | Les données ne se déplacent jamais. ObjectOS se connecte à votre base de données sur place. | +| « Nous ne pouvons pas risquer des écritures en production » | Liez les objets à une datasource en **lecture seule** (ou à un utilisateur de base de données en lecture seule). Analysez d'abord en toute sécurité ; activez les écritures délibérément. | +| « Modéliser chaque table prend des semaines » | Un agent de codage analyse le schéma et génère un fichier d'objet par table — vous révisez et affinez, vous ne tapez pas tout à la main. | +| « On ne peut pas confier nos données à l'IA » | Les agents s'exécutent en tant qu'**utilisateur connecté** et obéissent aux permissions au niveau des objets, des enregistrements et des champs. Ils ne voient jamais plus que la personne qui se trouve derrière eux. | +| « Nos données ne peuvent pas quitter notre réseau » | ObjectOS s'exécute dans votre environnement. Les données métier et les prompts restent à l'intérieur de votre périmètre. | + +## Générer des objets avec un agent de codage + +Le moyen le plus rapide d'importer un schéma existant est d'utiliser un +agent de codage (tel que Claude Code) pour **analyser les tables métier +et générer des définitions d'objets au niveau du code source** — un +fichier `*.object.ts` par table. + +L'application de référence [`hotcrm`](https://github.com/objectstack-ai/hotcrm) +montre la forme exacte que devrait prendre cette sortie : chaque table +devient un `src/objects/.object.ts` utilisant +`ObjectSchema.create({ … })` avec des définitions `Field.*` typées et +`Field.lookup(...)` pour les clés étrangères, assemblées par +`defineStack`. L'agent introspecte votre base de données connectée, +mappe les colonnes vers des types de champs, et écrit des objets que vous +possédez et committez. Vous gardez ce qui convient, supprimez les +colonnes que vous ne voulez pas exposer, et ajoutez par-dessus des +libellés, des validations et des permissions. + +Consultez [Data Sources](/docs/configure/data-sources) pour le guide de +création complet, étape par étape. + +## Ce que vous obtenez dès le premier jour + +Une fois les tables modélisées comme objets liés à votre base de données +existante : + +- **Analyse en langage naturel.** Les utilisateurs posent des questions + sur les enregistrements réels — « quelles affaires ont glissé ce + trimestre et qui en est responsable ? » — et la réponse est calculée + sur des données en direct via ObjectQL. +- **Automatisation gouvernée.** Les flux et actions peuvent lire et (là + où c'est autorisé) écrire les mêmes données, chaque étape étant + auditée. +- **Une API et une Console générées.** Les points de terminaison + REST/GraphQL et les écrans d'administration proviennent des mêmes + métadonnées — aucune couche d'intégration supplémentaire. +- **Un seul modèle de permissions.** La frontière qui s'applique aux + humains s'applique à l'identique au trafic IA. + +## Vers où cela se dirige + +Le parcours ci-dessus fonctionne aujourd'hui avec les briques déjà +livrées. Une expérience de **fédération clé en main** plus riche — +import de schéma en une étape, liaison de schéma détenu en externe et +garde-fous de sécurité intégrés — est en cours de conception active sous +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(statut : *Proposed*). En attendant son arrivée, le parcours documenté — +connecter, modéliser, lier, interroger — est la manière prise en charge +d'étendre un système existant. + +## Commencer ici + +- [Data Sources](/docs/configure/data-sources) — connecter une base de données, lier des objets, router les requêtes +- [AI Agents](/docs/build/agents) — des agents déclaratifs au-dessus de vos objets +- [Permissions](/docs/configure/permissions) — le modèle dont l'IA hérite +- [Quickstart](/docs/quickstart) — mettez un runtime en place en quelques minutes diff --git a/content/docs/extend-existing-systems.ja.mdx b/content/docs/extend-existing-systems.ja.mdx new file mode 100644 index 0000000..93917d0 --- /dev/null +++ b/content/docs/extend-existing-systems.ja.mdx @@ -0,0 +1,99 @@ +--- +title: 既存システムを拡張する +description: すでに運用しているビジネスシステムに ObjectOS を接続し、移行なしで AI ネイティブなクエリ、分析、自動化を追加します。 +--- + +# 既存システムを拡張する + +ObjectOS を評価しているほとんどのチームは、すでにシステムオブレコードを +持っています — CRM、ERP、チケッティングツール、あるいは本番の SQL や +MongoDB データベース上に乗った自前のバックオフィスです。問われるのは +たいてい「捨てて作り直すべきか?」ではありません。「すでにあるものを、 +リスクの高い移行なしに **AI ネイティブ** にできるか?」です。 + +このページが説明するのはまさにその道筋です。**ObjectOS を既存の +データベースに接続し、必要なテーブルをオブジェクトとしてモデル化し、 +AI エージェントにそのデータをクエリ・分析・操作させる** — あなたの権限の +もとで、あなたのインフラ上で、元のシステムには一切手を加えずに。 + +## この移行のかたち + +ビジネスシステムを置き換えるわけではありません。ObjectOS をその *隣に* +置き、同じデータベースを指し示します。 + +1. 既存のデータベースを [datasource](/docs/configure/data-sources) として + **接続** します。認証情報は環境から取得され、分析だけが目的なら接続を + 読み取り専用にできます。 +2. テーブルをオブジェクトとして **モデル化** します — 手作業でもよいですし、 + コーディングエージェントにスキーマをスキャンさせて、ソースレベルの + オブジェクトファイルを生成させることもできます。 +3. 各オブジェクトを datasource に **バインド** します(オブジェクトごとに、 + または名前空間全体に対するルーティングルールで)。 +4. **AI を使う** — テーブルがオブジェクトになった瞬間、あらゆる + エージェント、ツール、フロー、ダッシュボードがその上で動作し、適切な + データベースへ自動的にルーティングされます。 + +レガシーアプリケーションに関しては何も変わりません。行はそのままの場所に +留まります。ObjectOS はその上に乗る AI ネイティブで権限を意識したサーフェスに +なります。 + +## なぜ書き直しなしで機能するのか + +| 懸念 | ObjectOS の対処方法 | +|---|---| +| 「データを動かせない」 | データは一切動きません。ObjectOS はあなたのデータベースにそのまま接続します。 | +| 「本番への書き込みはリスクが大きすぎる」 | オブジェクトを **読み取り専用** の datasource(または読み取り専用の DB ユーザー)にバインドします。まず安全に分析し、書き込みは意図的に有効化します。 | +| 「すべてのテーブルをモデル化するのは何週間もかかる」 | コーディングエージェントがスキーマをスキャンし、テーブルごとに 1 つのオブジェクトファイルを生成します — あなたはレビューして調整するだけで、手入力はしません。 | +| 「AI に我々のデータを任せられない」 | エージェントは **サインインしたユーザー** として実行され、オブジェクト・レコード・フィールドレベルの権限に従います。その背後にいる人物以上のものを見ることは決してありません。 | +| 「我々のデータはネットワークの外に出せない」 | ObjectOS はあなたの環境内で動作します。ビジネスデータとプロンプトはあなたの境界の内側に留まります。 | + +## コーディングエージェントによるオブジェクトの生成 + +既存のスキーマを取り込む最速の方法は、コーディングエージェント +(Claude Code など)を使って **ビジネステーブルをスキャンし、ソースレベルの +オブジェクト定義を生成する** ことです — テーブルごとに 1 つの +`*.object.ts` ファイルです。 + +[`hotcrm`](https://github.com/objectstack-ai/hotcrm) リファレンスアプリは、 +その出力が取るべき正確なかたちを示しています。各テーブルは型付けされた +`Field.*` 定義と外部キー用の `Field.lookup(...)` を持つ +`src/objects/.object.ts` となり、`ObjectSchema.create({ … })` を使って +記述され、`defineStack` によって組み立てられます。エージェントは接続された +データベースを内省し、カラムをフィールド型にマッピングし、あなたが所有し +コミットするオブジェクトを書き出します。あなたは合うものを残し、公開したくない +カラムを削除し、その上にラベル、検証、権限を追加します。 + +完全なステップバイステップの作成ガイドについては +[Data Sources](/docs/configure/data-sources) を参照してください。 + +## 初日に得られるもの + +テーブルが既存のデータベースにバインドされたオブジェクトとしてモデル化 +されると、 + +- **自然言語による分析。** ユーザーは実際のレコードについて質問し — + 「今四半期にずれ込んだ案件はどれで、その担当者は誰か?」— その答えは + ObjectQL を通じてライブデータに対して計算されます。 +- **ガバナンスの効いた自動化。** フローとアクションは同じデータを読み取り、 + (許可されている場合は)書き込むことができ、すべてのステップが監査されます。 +- **生成された API と Console。** REST/GraphQL エンドポイントと管理画面は + 同じメタデータから生成されます — 余分な統合レイヤーは不要です。 +- **ひとつの権限モデル。** 人間に適用される境界が、AI トラフィックにも + まったく同じように適用されます。 + +## これからの方向性 + +上記のフローは、すでに出荷されているビルディングブロックで今日機能します。 +より豊かな **ターンキーのフェデレーション** 体験 — ワンステップの +スキーマインポート、外部所有スキーマのバインディング、組み込みの安全ゲート — +は [ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(ステータス: *Proposed*)のもとで活発に設計が進められています。それが +実現するまでは、文書化された道筋 — 接続、モデル化、バインド、クエリ — +が既存システムを拡張するサポート済みの方法です。 + +## ここから始める + +- [Data Sources](/docs/configure/data-sources) — データベースを接続し、オブジェクトをバインドし、クエリをルーティングする +- [AI Agents](/docs/build/agents) — オブジェクト上で動作する宣言的なエージェント +- [Permissions](/docs/configure/permissions) — AI が継承するモデル +- [Quickstart](/docs/quickstart) — 数分でランタイムを立ち上げる diff --git a/content/docs/extend-existing-systems.ko.mdx b/content/docs/extend-existing-systems.ko.mdx new file mode 100644 index 0000000..23478b3 --- /dev/null +++ b/content/docs/extend-existing-systems.ko.mdx @@ -0,0 +1,97 @@ +--- +title: 기존 시스템 확장하기 +description: 이미 운영 중인 비즈니스 시스템에 ObjectOS를 연결한 다음, 마이그레이션 없이 AI 네이티브 쿼리, 분석, 자동화를 더하세요. +--- + +# 기존 시스템 확장하기 + +ObjectOS를 검토하는 대부분의 팀은 이미 기록 시스템을 가지고 있습니다 — CRM, +ERP, 티켓팅 도구, 프로덕션 SQL 또는 MongoDB 데이터베이스 위에 자리한 자체 +제작 백오피스 같은 것들입니다. 문제는 좀처럼 "이것을 버리고 다시 만들어야 +할까?"가 아닙니다. 그것은 "위험한 마이그레이션 없이, 이미 가지고 있는 것을 +**AI 네이티브**로 만들 수 있을까?"입니다. + +이 페이지가 설명하는 것이 바로 그 길입니다: **기존 데이터베이스에 ObjectOS를 +연결하고, 관심 있는 테이블을 객체로 모델링한 다음, AI 에이전트가 그 데이터를 +쿼리하고 분석하고 그에 따라 행동하게 하는 것** — 여러분의 권한 아래에서, +여러분의 인프라 위에서, 원래 시스템은 그대로 둔 채로 말입니다. + +## 이 변화의 형태 + +여러분은 비즈니스 시스템을 대체하지 않습니다. ObjectOS를 그 *옆에* 두고 +동일한 데이터베이스를 가리키게 합니다: + +1. 기존 데이터베이스를 [데이터소스](/docs/configure/data-sources)로 + **연결**합니다. 자격 증명은 환경에서 가져오며, 분석만 하려는 경우 + 연결은 읽기 전용일 수 있습니다. +2. 테이블을 객체로 **모델링**합니다 — 직접 손으로 하거나, 코딩 에이전트가 + 스키마를 스캔하여 소스 수준의 객체 파일을 여러분을 위해 생성하게 + 합니다. +3. 각 객체를 데이터소스에 **바인딩**합니다(객체별로, 또는 네임스페이스 + 전체에 대한 라우팅 규칙으로). +4. **AI를 사용**합니다 — 테이블이 객체가 되는 순간, 모든 에이전트, 도구, + 플로우, 대시보드가 그 위에서 작동하며, 자동으로 올바른 데이터베이스로 + 라우팅됩니다. + +레거시 애플리케이션에 대해서는 아무것도 변하지 않습니다. 행은 있던 자리에 +그대로 있습니다. ObjectOS는 그 위에 얹히는 AI 네이티브, 권한 인식 표면이 +됩니다. + +## 다시 작성하지 않고도 이것이 작동하는 이유 + +| 우려 사항 | ObjectOS가 처리하는 방식 | +|---|---| +| "데이터를 옮길 수 없습니다" | 데이터는 절대 옮겨지지 않습니다. ObjectOS는 여러분의 데이터베이스에 있는 그대로 연결합니다. | +| "프로덕션에 쓰기를 감행할 수 없습니다" | 객체를 **읽기 전용** 데이터소스(또는 읽기 전용 DB 사용자)에 바인딩하세요. 먼저 안전하게 분석하고, 쓰기는 신중하게 활성화하세요. | +| "모든 테이블을 모델링하는 것은 몇 주의 작업입니다" | 코딩 에이전트가 스키마를 스캔하여 테이블당 객체 파일 하나를 생성합니다 — 여러분은 검토하고 다듬을 뿐, 손으로 타이핑하지 않습니다. | +| "AI에게 우리 데이터를 맡길 수 없습니다" | 에이전트는 **로그인한 사용자**로 실행되며 객체, 레코드, 필드 수준의 권한을 따릅니다. 그들은 그 뒤에 있는 사람보다 더 많은 것을 결코 보지 못합니다. | +| "우리 데이터는 네트워크를 벗어날 수 없습니다" | ObjectOS는 여러분의 환경에서 실행됩니다. 비즈니스 데이터와 프롬프트는 여러분의 경계 내부에 머뭅니다. | + +## 코딩 에이전트로 객체 생성하기 + +기존 스키마를 가져오는 가장 빠른 방법은 코딩 에이전트(예: Claude Code)를 +사용하여 **비즈니스 테이블을 스캔하고 소스 수준의 객체 정의를 생성**하는 +것입니다 — 테이블당 `*.object.ts` 파일 하나씩. + +[`hotcrm`](https://github.com/objectstack-ai/hotcrm) 레퍼런스 앱은 그 +결과물이 취해야 할 정확한 형태를 보여줍니다: 각 테이블은 타입이 지정된 +`Field.*` 정의와 외래 키를 위한 `Field.lookup(...)`를 사용하는 +`ObjectSchema.create({ … })`로 `src/objects/.object.ts`가 되며, +`defineStack`으로 조립됩니다. 에이전트는 연결된 데이터베이스를 인트로스펙트하고, +컬럼을 필드 타입에 매핑하며, 여러분이 소유하고 커밋하는 객체를 작성합니다. +맞는 것은 유지하고, 노출하고 싶지 않은 컬럼은 버리며, 그 위에 레이블, +유효성 검사, 권한을 추가합니다. + +전체 단계별 작성 가이드는 [데이터소스](/docs/configure/data-sources)를 +참조하세요. + +## 첫날에 얻는 것 + +테이블이 기존 데이터베이스에 바인딩된 객체로 모델링되고 나면: + +- **자연어 분석.** 사용자가 실제 레코드에 대해 질문하고 — "이번 분기에 + 지연된 거래는 어떤 것이고 누가 담당인가요?" — 그 답은 ObjectQL을 통해 + 실시간 데이터에 대해 계산됩니다. +- **거버넌스가 적용된 자동화.** 플로우와 액션은 동일한 데이터를 읽고 + (허용된 경우) 쓸 수 있으며, 모든 단계가 감사됩니다. +- **생성된 API와 Console.** REST/GraphQL 엔드포인트와 관리 화면이 동일한 + 메타데이터에서 나옵니다 — 추가 통합 계층이 없습니다. +- **하나의 권한 모델.** 사람에게 적용되는 경계가 AI 트래픽에도 동일하게 + 적용됩니다. + +## 이것이 향하는 곳 + +위의 흐름은 이미 출시된 빌딩 블록으로 오늘날 작동합니다. 더 풍부한 +**턴키 페더레이션** 경험 — 한 단계 스키마 가져오기, 외부 소유 스키마 +바인딩, 내장 안전 게이트 — 는 +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +아래에서 활발히 설계 중입니다(상태: *Proposed*). 그것이 도착하기 전까지, +문서화된 경로 — 연결, 모델링, 바인딩, 쿼리 — 가 기존 시스템을 확장하는 +지원되는 방식입니다. + +## 여기서 시작하세요 + +- [데이터소스](/docs/configure/data-sources) — 데이터베이스 연결, 객체 바인딩, 쿼리 라우팅 +- [AI 에이전트](/docs/build/agents) — 여러분의 객체 위에서 작동하는 선언적 에이전트 +- [권한](/docs/configure/permissions) — AI가 상속하는 모델 +- [Quickstart](/docs/quickstart) — 몇 분 안에 런타임을 띄우기 diff --git a/content/docs/extend-existing-systems.mdx b/content/docs/extend-existing-systems.mdx new file mode 100644 index 0000000..78aab9b --- /dev/null +++ b/content/docs/extend-existing-systems.mdx @@ -0,0 +1,93 @@ +--- +title: Extend Existing Systems +description: Connect ObjectOS to the business systems you already run, then add AI-native query, analysis, and automation — without a migration. +--- + +# Extend Existing Systems + +Most teams evaluating ObjectOS already have a system of record — a CRM, an +ERP, a ticketing tool, a homegrown back office sitting on a production +SQL or MongoDB database. The question is rarely "should we throw it away +and rebuild?" It's "can we make the thing we already have **AI-native**, +without a risky migration?" + +That's exactly the path this page describes: **connect ObjectOS to your +existing database, model the tables you care about as objects, and let AI +agents query, analyze, and act on that data** — under your permissions, +on your infrastructure, with the original system untouched. + +## The shape of the move + +You don't replace your business system. You put ObjectOS *next to* it and +point it at the same database: + +1. **Connect** the existing database as a [datasource](/docs/configure/data-sources). + Credentials come from your environment; the connection can be + read-only if you only want to analyze. +2. **Model** the tables as objects — by hand, or by letting a coding + agent scan the schema and generate source-level object files for you. +3. **Bind** each object to the datasource (per object, or with a routing + rule for a whole namespace). +4. **Use AI** — the moment a table is an object, every agent, tool, flow, + and dashboard works on it, routed automatically to the right database. + +Nothing about the legacy application changes. The rows stay where they +are. ObjectOS becomes the AI-native, permission-aware surface on top. + +## Why this works without a rewrite + +| Concern | How ObjectOS handles it | +|---|---| +| "We can't move the data" | The data never moves. ObjectOS connects to your database in place. | +| "We can't risk writes to production" | Bind objects to a **read-only** datasource (or a read-only DB user). Analyze safely first; enable writes deliberately. | +| "Modeling every table is weeks of work" | A coding agent scans the schema and generates one object file per table — you review and refine, you don't hand-type. | +| "AI can't be trusted with our data" | Agents run as the **signed-in user** and obey object-, record-, and field-level permissions. They never see more than the person behind them. | +| "Our data can't leave our network" | ObjectOS runs in your environment. Business data and prompts stay inside your perimeter. | + +## Generating objects with a coding agent + +The fastest way to bring an existing schema in is to use a coding agent +(such as Claude Code) to **scan the business tables and generate +source-level object definitions** — one `*.object.ts` file per table. + +The [`hotcrm`](https://github.com/objectstack-ai/hotcrm) reference app +shows the exact shape that output should take: each table becomes a +`src/objects/.object.ts` using `ObjectSchema.create({ … })` with +typed `Field.*` definitions and `Field.lookup(...)` for foreign keys, +assembled by `defineStack`. The agent introspects your connected +database, maps columns to field types, and writes objects you own and +commit. You keep what fits, drop columns you don't want exposed, and add +labels, validations, and permissions on top. + +See [Data Sources](/docs/configure/data-sources) for the full, +step-by-step authoring guide. + +## What you get on day one + +Once the tables are modeled as objects bound to your existing database: + +- **Natural-language analysis.** Users ask questions about the real + records — "which deals slipped this quarter and who owns them?" — and + the answer is computed against live data through ObjectQL. +- **Governed automation.** Flows and actions can read and (where allowed) + write the same data, with every step audited. +- **A generated API and Console.** REST/GraphQL endpoints and admin + screens come from the same metadata — no extra integration layer. +- **One permission model.** The boundary that applies to humans applies + identically to AI traffic. + +## Where this is headed + +The flow above works today with shipped building blocks. A richer, +**turn-key federation** experience — one-step schema import, externally +owned schema binding, and built-in safety gates — is in active design +under [ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +(status: *Proposed*). Until it lands, the documented path — connect, +model, bind, query — is the supported way to extend an existing system. + +## Start here + +- [Data Sources](/docs/configure/data-sources) — connect a database, bind objects, route queries +- [AI Agents](/docs/build/agents) — declarative agents over your objects +- [Permissions](/docs/configure/permissions) — the model AI inherits +- [Quickstart](/docs/quickstart) — stand up a runtime in minutes diff --git a/content/docs/extend-existing-systems.zh-Hans.mdx b/content/docs/extend-existing-systems.zh-Hans.mdx new file mode 100644 index 0000000..3fac3a0 --- /dev/null +++ b/content/docs/extend-existing-systems.zh-Hans.mdx @@ -0,0 +1,91 @@ +--- +title: 扩展现有系统 +description: 将 ObjectOS 连接到你已经在运行的业务系统,然后为其加上 AI 原生的查询、分析与自动化能力 —— 无需迁移。 +--- + +# 扩展现有系统 + +大多数评估 ObjectOS 的团队都已经有一套记录系统 —— 一个 CRM、一个 +ERP、一个工单工具,或者一个跑在生产 SQL 或 MongoDB 数据库上的自研后办公 +系统。问题很少是 "我们是否应该把它丢掉重建?",而是 "我们能不能让已有的 +东西变得 **AI 原生**,又不必经历一次有风险的迁移?" + +这正是本页所描述的路径:**将 ObjectOS 连接到你 +现有的数据库,把你关心的表建模为对象,并让 AI +Agent 查询、分析这些数据并对其采取行动** —— 在你的权限之下、 +在你的基础设施之上,且原系统毫发无损。 + +## 这一步的形状 + +你不会替换你的业务系统。你把 ObjectOS 放在它*旁边*, +让二者指向同一个数据库: + +1. **连接**:把现有数据库作为[数据源](/docs/configure/data-sources)接入。 + 凭据来自你的环境;如果你只想做分析,连接可以是 + 只读的。 +2. **建模**:把表建模为对象 —— 手工进行,或者让一个编码 + Agent 扫描 schema 并为你生成源码级的对象文件。 +3. **绑定**:把每个对象绑定到数据源(按对象绑定,或用针对 + 整个命名空间的路由规则)。 +4. **使用 AI**:一旦某张表成为对象,每个 Agent、工具、流程 + 和仪表盘都能在它之上工作,并被自动路由到正确的数据库。 + +遗留应用的任何方面都不会改变。数据行保持原位。 +ObjectOS 成为其上层那个 AI 原生、感知权限的界面。 + +## 为什么这样行得通且无需重写 + +| 顾虑 | ObjectOS 如何应对 | +|---|---| +| "我们没法移动数据" | 数据从不移动。ObjectOS 就地连接到你的数据库。 | +| "我们不能冒险对生产库写入" | 把对象绑定到**只读**数据源(或一个只读的数据库用户)。先安全地分析;再有意识地开启写入。 | +| "把每张表都建模需要几周时间" | 一个编码 Agent 扫描 schema,并为每张表生成一个对象文件 —— 你审阅并打磨,而不是逐个手敲。 | +| "AI 不能托付我们的数据" | Agent 以**登录用户**的身份运行,并遵守对象级、记录级和字段级权限。它们看到的内容绝不会超过其背后的那个人。 | +| "我们的数据不能离开网络" | ObjectOS 运行在你的环境里。业务数据与提示词都留在你的边界之内。 | + +## 用编码 Agent 生成对象 + +把一个现有 schema 引入进来最快的方式,是使用一个编码 Agent +(例如 Claude Code)来**扫描业务表并生成 +源码级的对象定义** —— 每张表对应一个 `*.object.ts` 文件。 + +[`hotcrm`](https://github.com/objectstack-ai/hotcrm) 参考应用 +展示了这类输出应当呈现的确切形态:每张表都成为一个 +`src/objects/.object.ts`,使用 `ObjectSchema.create({ … })` 配合 +带类型的 `Field.*` 定义,并用 `Field.lookup(...)` 表示外键, +由 `defineStack` 组装。该 Agent 内省你已连接的 +数据库,把列映射到字段类型,并写出归你所有、由你 +提交的对象。你保留合适的部分,丢弃不想暴露的列,并在其上 +添加标签、校验与权限。 + +完整的、逐步的编写指南见[数据源](/docs/configure/data-sources)。 + +## 第一天你就能得到什么 + +一旦这些表被建模为绑定到你现有数据库的对象: + +- **自然语言分析。** 用户就真实记录提问 —— + "这个季度哪些交易延期了,分别归谁负责?" —— + 答案会通过 ObjectQL 针对实时数据计算得出。 +- **受治理的自动化。** 流程与动作可以读取并(在允许时) + 写入同一份数据,每一步都被审计。 +- **生成的 API 与 Console。** REST/GraphQL 端点与管理 + 界面来自同一份元数据 —— 无需额外的集成层。 +- **统一的权限模型。** 适用于人类的边界,会原封不动地 + 同样适用于 AI 流量。 + +## 它的走向 + +上述流程在今天就能通过已发布的构建块运转。一种更丰富的、 +**开箱即用的联邦**体验 —— 一步式 schema 导入、外部 +拥有的 schema 绑定,以及内建的安全闸门 —— 正在 +[ADR-0015](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0015-external-datasource-federation.md) +下积极设计中(状态:*Proposed*)。在它落地之前,这条已记录的 +路径 —— 连接、建模、绑定、查询 —— 就是扩展现有系统的受支持方式。 + +## 从这里开始 + +- [数据源](/docs/configure/data-sources) —— 连接数据库、绑定对象、路由查询 +- [AI Agent](/docs/build/agents) —— 构建在你对象之上的声明式 Agent +- [权限](/docs/configure/permissions) —— AI 所继承的模型 +- [Quickstart](/docs/quickstart) —— 几分钟内搭起一个运行时 diff --git a/content/docs/index.ko.mdx b/content/docs/index.ko.mdx new file mode 100644 index 0000000..9687b0a --- /dev/null +++ b/content/docs/index.ko.mdx @@ -0,0 +1,94 @@ +--- +title: ObjectOS +description: 네트워크 안에 머무는 내부 도구용 런타임. 한 번의 명령으로 시작하며, 데이터베이스도, 인증도, 데이터도 모두 당신의 것 — 결코 우리의 것이 아닙니다. +--- + +# ObjectOS + +**ObjectOS는 데이터를 포기하지 않고 내부 도구, 관리자 패널, 백오피스 앱을 +구축할 수 있는 셀프 호스팅 런타임입니다.** Console 내장 AI Builder에 +필요한 것을 설명하거나 템플릿을 포크하면, REST API, 자동 생성된 관리자 +UI, 인증, RBAC, 감사 로그, 파일 저장소, 백그라운드 작업, 웹훅, AI 통합을 +얻을 수 있습니다. 이 모든 것이 당신의 네트워크에서, 당신의 데이터베이스 +위에서 실행됩니다. + +이는 Retool, Supabase, Salesforce가 자녀를 낳아 그 자녀가 당신의 방화벽 +안에서 실행되는 것과 같습니다 — 게다가 앱을 대신 만들어 주는 AI까지 +딸려 있습니다. + +## 60초 만에 실행하기 + +```bash +npm i -g @objectstack/cli +os start +``` + +**http://localhost:3000** 을 열면 Console와 Account, 감사 로그, 그리고 +SQLite 데이터베이스를 갖춘 작동하는 ObjectOS가 준비됩니다 — 구성도, +스캐폴딩도 전혀 필요 없습니다. + +그런 다음 다음 중 하나를 하면 됩니다. + +- **AI Builder와 대화하세요** — *"우선순위와 담당자가 있는 지원 + 티켓을 추적해야 합니다"* — 그러면 메타데이터가 생성되는 것을 지켜볼 + 수 있습니다. [Build → AI Builder](/docs/build/ai-builder)를 참고하세요. +- Console 내장 marketplace에서 **템플릿을 설치하세요**(Todo, + Contracts, Helpdesk 등). 한 번의 클릭으로 실제 앱을 갖출 수 있습니다. +- TypeScript 소스를 직접 관리하고 싶다면 + **[템플릿](/docs/build/templates)을 포크하세요**. + +## 왜 다른 것이 아니라 ObjectOS인가 … + +| 현재 사용 중인 것 | 불편한 점 | ObjectOS가 제공하는 것 | +|---|---|---| +| **Retool / Appsmith** | UI 빌더는 훌륭하지만 데이터가 그들의 클라우드에 있고 가격이 사용자 수에 따라 늘어납니다 | 데이터가 네트워크를 절대 벗어나지 않습니다. Apache-2.0이며 좌석당 과금이 없습니다 | +| **Supabase / Firebase** | 백엔드 서비스(BaaS)는 빠르지만 벤더에 종속되며 설계상 멀티테넌트 SaaS입니다 | 동일한 개발자 경험(자동 API, 인증, 저장소)을 제공하지만 런타임과 데이터베이스를 당신이 소유합니다 | +| **Salesforce / NetSuite** | 강력한 플랫폼이지만 커스터마이징이 고통스럽고 좌석당 비용이 눈물이 날 정도입니다 | 동일한 메타데이터 기반 모델(객체, 필드, 역할, 공유 규칙)을 셀프 호스팅으로 제공하며 좌석당 과금이 없습니다 | +| **직접 구축(Next + Prisma + NextAuth)** | 매번 RBAC, 감사, 파일 업로드, 설정, 웹훅, 작업을 다시 만들어야 합니다 | 실제 비즈니스 로직만 출시하면 됩니다. 플랫폼 기반 작업은 이미 마련되어 있습니다 | + +## 기본으로 제공되는 것 + +| 영역 | 무엇인가 | +|---|---| +| **자동 REST API** | 선언하는 모든 객체에 필터/정렬/페이지네이션이 가능한 `/api/v1/data/`가 생깁니다 | +| **Console** (`/_console/`) | 자동 생성된 관리자 UI: 레코드 조회 및 편집, 사용자/역할/권한 집합 관리, 감사 로그·세션·API 키·설정 보기 | +| **Account** (`/_account/`) | 로그인, 회원가입, 비밀번호 재설정, OAuth, OIDC/SSO, 패스키, 2FA | +| **20개 이상의 플러그인** | RBAC + 행 수준 보안, 감사, 파일 저장소, 큐, 작업, 이메일, AI, 웹훅 — 선언적이며 필요에 따라 사용 | + +## 당신이 계속 통제하는 것 + +| 자산 | 어디에 있는가 | +|---|---| +| 비즈니스 데이터 | **당신의** 데이터베이스(Postgres, SQLite, MySQL, MongoDB) | +| 사용자 신원 및 세션 | **당신의** 데이터베이스 | +| 감사 로그 | **당신의** 데이터베이스 | +| 파일 | **당신의** 저장소(로컬 디스크, S3, 또는 R2/MinIO 같은 S3 호환 저장소) | +| 비밀 값 | **당신의** 시크릿 매니저 | +| 런타임 자체 | **당신의** 서버, 컨테이너, 또는 노트북 | + +ObjectOS는 절대 외부로 연결을 시도하지 않습니다. 텔레메트리도, 라이선스 +서버도 없습니다. 에어갭(air-gapped) 네트워크는 일급 배포 대상입니다 — +[Air-gapped](/docs/deploy/air-gapped)를 참고하세요. + +## 다음으로 갈 곳 + +| 하고 싶은 것 … | 읽을 문서 | +|---|---| +| 실제로 작동하는 모습 보기 | [Quickstart](/docs/quickstart) | +| 내부에 무엇이 있는지 이해하기 | [Architecture](/docs/architecture) | +| Docker로 실행하기 | [Docker](/docs/deploy/docker) | +| Kubernetes에 배포하기 | [Kubernetes](/docs/deploy/kubernetes) | +| Postgres / 당신의 DB에 연결하기 | [Runtime Configuration](/docs/configure/runtime) | +| SSO 설정하기 | [Authentication](/docs/configure/authentication) | +| 누가 무엇을 볼지 잠그기 | [Permissions](/docs/configure/permissions) | +| 다른 서비스에서 호출하기 | [API Access](/docs/configure/api-access) | +| 다른 시스템으로 이벤트 보내기 | [Webhooks](/docs/configure/webhooks) | +| 프로덕션으로 가기 | [Production Readiness](/docs/operate/production) | + +## 라이선스 및 가격 + +ObjectOS 런타임은 **Apache-2.0**입니다 — 가장 널리 쓰이는 관대한 OSS +라이선스입니다. 상업용 제품에 사용하고, 임베드하고, 재배포하고, 수정 +사항을 비공개로 유지할 수 있습니다. 좌석당 요금도, 라이선스 서버도, "연 +$50K부터 시작" 같은 것도 없습니다. 선택적 상업 지원 및 관리형 호스팅은 +별도로 제공됩니다 — [License](/docs/resources/license)를 참고하세요. diff --git a/content/docs/meta.json b/content/docs/meta.json index 6e9ac11..f9ebc3f 100644 --- a/content/docs/meta.json +++ b/content/docs/meta.json @@ -3,6 +3,7 @@ "pages": [ "index", "why", + "extend-existing-systems", "quickstart", "architecture", "deploy", diff --git a/content/docs/operate/backup.ko.mdx b/content/docs/operate/backup.ko.mdx new file mode 100644 index 0000000..d69d5ec --- /dev/null +++ b/content/docs/operate/backup.ko.mdx @@ -0,0 +1,155 @@ +--- +title: 백업 및 재해 복구 +description: 무엇을 백업하고, 어떻게 복원하며, 장애에 어떻게 대비할 것인가. +--- + +# 백업 및 재해 복구 + +ObjectOS 자체는 상태를 저장하지 않습니다(stateless). 런타임은 컨테이너 +이미지로부터 재구축할 수 있습니다. **백업은 ObjectOS 자체가 아니라 +ObjectOS가 의존하는 데이터에 관한 것입니다.** 복구는 이러한 데이터셋을 +중심으로 계획하세요. + +## 무엇을 백업할 것인가 + +| 자산 | 소유 주체 | 백업 전략 | +|---|---|---| +| 비즈니스 데이터베이스 | 고객 | 데이터베이스 네이티브 백업(관리형 서비스의 경우 PITR), 분기마다 복원 테스트 수행 | +| 컴파일된 아티팩트(`objectstack.json`) | 애플리케이션/릴리스 팀 | 게시된 모든 아티팩트를 변경 불가능한 스토리지에 저장, 절대 덮어쓰지 않음 | +| 시크릿 기준선 | 고객의 시크릿 관리자 | 벤더 네이티브 백업, 정책에 따른 교체 주기 | +| 객체/파일 스토리지(`storage` 기능이 활성화된 경우) | 고객 | 버킷 버전 관리 + 필요 시 리전 간 복제 | +| 고객이 관리하는 ID 공급자 | IdP 벤더 | 벤더 네이티브 | + +ObjectOS는 컨테이너 파일시스템에 대한 별도의 백업을 **요구하지 +않습니다**. 캐시 디렉터리(`OS_CACHE_DIR`)는 재구축이 가능합니다. + +## 데이터베이스 백업 + +전략을 드라이버에 맞추세요: + +| 드라이버 | 권장 접근 방식 | +|---|---| +| PostgreSQL | 지속적인 WAL 아카이빙 + 베이스 백업, 시점 복원(point-in-time restore) | +| MySQL | 바이너리 로그 아카이빙 + 전체 덤프, 시점 복원 | +| MongoDB | 레플리카 세트 스냅샷, PITR를 위한 oplog | +| SQLite(단일 노드 / 데스크톱) | WAL 모드 + `VACUUM INTO` 또는 Online Backup API를 통한 핫 스냅샷, 필요 시 Litestream으로 지속적 복제 — 아래 [SQLite 배포](#sqlite-deployments) 참조 | + +드라이버와 관계없이 다음을 검증하세요: + +- 백업이 고객의 RPO 내에 완료되는지, +- 동일한 아티팩트로 새 데이터베이스에서 ObjectOS를 부팅하여 트래픽을 + 처리할 수 있는지, +- 복원 테스트가 적어도 분기에 한 번 엔드 투 엔드로 수행되는지. + +## SQLite 배포 + +SQLite는 기본 드라이버이며 **단일 노드** ObjectOS — 데스크톱 앱, +소규모 팀용 내부 도구, 엣지 / 온프레미스 어플라이언스, 평가 환경 — 에서 +정당한 프로덕션 선택지입니다. 트레이드오프는 구조적입니다. SQLite는 +단일 쓰기(single-writer)이므로 멀티 노드 ObjectOS, 높은 동시 쓰기 +처리량, 또는 공유 데이터베이스 배포에는 적합하지 않습니다. 그러한 +경우에는 PostgreSQL을 사용하세요. + +SQLite를 프로덕션에서 실행할 때는, 안전하게 만드는 구성과 백업 +레시피가 함께 갑니다: + +**런타임 구성** + +- WAL 활성화: `PRAGMA journal_mode=WAL`(리더가 라이터를 차단하지 않으며, + 크래시에 안전함). +- `PRAGMA synchronous=NORMAL`은 일반적인 데스크톱/단일 노드 기본값입니다 — + WAL과 함께 사용하면 안전하고 `FULL`보다 훨씬 빠릅니다. +- 데이터베이스 파일은 **로컬 디스크**에 두세요. 런타임이 실행 중인 동안에는 + NFS, SMB, 또는 Dropbox / OneDrive / iCloud로 동기화되는 폴더 안에 + **두지 마세요** — 이러한 환경에서는 SQLite의 잠금이 불안정하며, 이것이 + SQLite 배포가 손상되는 가장 흔한 원인입니다. + +**핫 스냅샷(다운타임 없음)** + +런타임은 SQLite의 Online Backup API 또는 `VACUUM INTO`를 사용하여 +스냅샷을 찍는 동안에도 트래픽을 계속 처리합니다: + +```sql +VACUUM INTO '/var/backups/objectos/db-2026-05-27T13-00Z.sqlite'; +``` + +RPO에 맞는 주기로(cron, systemd 타이머, 또는 인프로세스 스케줄러) +이를 예약하세요. 각 스냅샷에 이를 생성한 아티팩트 버전을 태그하여 +데이터베이스와 아티팩트를 함께 롤백할 수 있도록 하세요 +(아래 [아티팩트 버전 관리](#artifact-versioning) 참조). + +**오프사이트 복사본** + +로컬 스냅샷은 디스크 손실에서 살아남지 못합니다. 각 스냅샷을 내구성 +있는 스토리지로 푸시하세요: + +- 서버 / VM: S3 또는 S3 호환 버킷으로 푸시하세요(이미 `storage` 기능을 + 위해 하나 구성되어 있을 가능성이 높습니다). +- 데스크톱 앱: 스냅샷을 사용자가 제어하는 동기화 폴더(OneDrive / + iCloud / Google Drive)에 기록하세요 — 스냅샷 파일은 라이브 + 데이터베이스와 달리 **닫혀 있고 변경 불가능**하므로 여기서는 + 허용됩니다. + +**지속적 복제(낮은 RPO)** + +SQLite를 포기하지 않으면서 1분 미만의 RPO를 원한다면, ObjectOS 프로세스와 +함께 [Litestream](https://litestream.io)을 실행하세요. WAL을 S3 호환 +스토리지로 스트리밍하고 시점 복원을 지원합니다. 단일 노드 SQLite 배포가 +거의 제로에 가까운 데이터 손실을 필요로 할 때 권장되는 방법입니다. + +**복원** + +런타임을 중지하고, 데이터베이스 파일을 선택한 스냅샷으로 교체한 뒤 +(또는 `litestream restore` 실행), 해당 스냅샷을 생성한 동일한 아티팩트 +버전으로 런타임을 시작하세요. + +## 아티팩트 버전 관리 + +게시된 아티팩트는 변경 불가능한 것으로 취급하세요. 각 아티팩트에 이를 +컴파일하는 데 사용된 릴리스 id를 태그하세요(예: `objectstack-2026-05-24.json`). +알려진 정상 비즈니스 상태로의 복구는 보통 다음을 의미합니다: + +1. 데이터베이스를 선택한 시점으로 복원합니다. +2. ObjectOS를 그 시점에 활성화되어 있던 아티팩트 버전으로 다시 + 가리킵니다. +3. 런타임을 다시 시작합니다. + +아티팩트를 제자리에서 덮어쓰면, 데이터베이스 백업이 완벽하더라도 +깔끔하게 롤백할 수 있는 능력을 잃게 됩니다. + +## RPO/RTO 계획 + +고객 배포를 위한 실용적인 시작 목표: + +| 등급 | RPO | RTO | 비고 | +|---|---|---|---| +| 평가 / 데모 | 최선의 노력 | 최선의 노력 | SQLite 스냅샷으로 충분 | +| 데스크톱 앱 / 소규모 팀 단일 노드 | ≤ 1시간(스냅샷) 또는 ≤ 수 초(Litestream) | 수 분 | SQLite + WAL + 예약된 `VACUUM INTO` + 오프사이트 복사본 | +| 단일 테넌트 프로덕션 | ≤ 15분 | ≤ 1시간 | PITR가 있는 관리형 PostgreSQL + 웜 이미지 | +| 멀티 테넌트 / 규제 대상 | ≤ 5분 | ≤ 30분 | HA 데이터베이스 + 멀티 AZ ObjectOS + 테스트된 런북 | + +더 엄격한 목표를 달성하려면 ObjectOS 컨테이너 자체 외부의 플랫폼 +변경(HA 데이터베이스, 멀티 AZ 인그레스, 웜 레플리카)이 필요합니다. + +## 연습해 볼 만한 장애 모드 + +- **데이터베이스가 장시간 사용 불가.** 런타임이 명확한 503을 표출하고 + 프로브가 파드를 올바르게 비정상으로 표시하는지 확인하세요. +- **아티팩트 회귀.** 아티팩트 포인터를 롤백하세요. 데이터는 영향을 + 받지 않습니다. +- **시크릿 교체.** `AUTH_SECRET`을 교체하면 모든 세션이 무효화됩니다. + 유지보수 시간대에 실행하거나 레플리카 간에 시차를 두고 진행하세요. +- **리전 장애.** 고객이 리전 페일오버를 요구하는 경우, 비즈니스 + 데이터베이스, 시크릿 관리자, 인그레스가 모두 리전 간으로 구성되어야 + 합니다. ObjectOS 자체는 이미지를 사용할 수 있는 곳이라면 어디서든 + 실행할 수 있습니다. + +## 런북에 담아야 할 것 + +- 각 데이터셋에 대한 백업 일정, 보존 기간, 온콜 담당자. +- 단계별 복원 절차(데이터베이스 먼저, 그다음 아티팩트, 그다음 ObjectOS + 시작). +- 복원된 데이터베이스가 일관적인지 확인하기 위한 검증 쿼리. +- ObjectOS 이미지와 아티팩트 버전 모두에 대한 롤백 계획(자세한 내용은 + [업그레이드 및 롤백](/docs/operate/upgrade) 참조). +- 고객에게 노출되는 인시던트를 위한 커뮤니케이션 템플릿. diff --git a/content/docs/operate/meta.ko.json b/content/docs/operate/meta.ko.json new file mode 100644 index 0000000..400477f --- /dev/null +++ b/content/docs/operate/meta.ko.json @@ -0,0 +1,11 @@ +{ + "title": "운영", + "defaultOpen": false, + "pages": [ + "production", + "observability", + "backup", + "upgrade", + "troubleshooting" + ] +} diff --git a/content/docs/operate/observability.ko.mdx b/content/docs/operate/observability.ko.mdx new file mode 100644 index 0000000..60c4e5c --- /dev/null +++ b/content/docs/operate/observability.ko.mdx @@ -0,0 +1,113 @@ +--- +title: 가시성(Observability) +description: 로그, 요청 ID, 메트릭, 오류, 세션, 감사 로그. +--- + +# 가시성(Observability) + +ObjectOS 운영에는 인프라 신호와 애플리케이션 신호가 모두 필요합니다. 프레임워크는 +request-id, 메트릭, 오류 리포터, 감사 프리미티브를 제공하며, 이를 어디로 내보낼지는 +배포 환경에서 결정합니다. + +## 요청 식별자 + +모든 프로덕션 배포는 요청 ID를 전파하거나 생성해야 합니다. + +```text +X-Request-Id +``` + +이를 사용해 다음을 상호 연관시킵니다. + +- 인그레스 로그; +- ObjectOS 로그; +- 데이터베이스 슬로우 쿼리; +- 오류 보고서; +- 고객 지원 티켓. + +## 메트릭과 오류 + +런타임은 플러그형 메트릭 및 오류 리포터 인터페이스를 노출합니다. 이를 Prometheus, +OpenTelemetry, Datadog, Sentry 또는 그 밖에 승인된 백엔드 등 고객이 선택한 시스템에 +연결하십시오. + +최소한 다음 항목을 추적하십시오. + +| 신호 | 이유 | +|---|---| +| 라우트/상태별 요청 수 | 오류 급증 감지 | +| 요청 소요 시간 | 지연 시간 회귀 감지 | +| 5xx 오류 | 런타임 장애에 대한 알림 | +| 인증 실패 | 구성 오류 또는 공격 패턴 감지 | +| Artifact/kernel 캐시 미스 | 콜드 스타트 동작 이해 | + +### 최소 Prometheus 예제 + +메트릭 서비스가 활성화되면 ObjectOS는 Prometheus 호환 메트릭 엔드포인트를 노출합니다. + +```yaml +# prometheus.yml +scrape_configs: + - job_name: objectos + metrics_path: /metrics + static_configs: + - targets: ['objectos:3000'] +``` + +유용한 시작용 알림: + +```yaml +groups: + - name: objectos + rules: + - alert: ObjectOS5xxSpike + expr: | + sum(rate(http_requests_total{status=~"5.."}[5m])) + / sum(rate(http_requests_total[5m])) > 0.02 + for: 5m + annotations: + summary: "ObjectOS 5xx rate above 2% for 5 minutes" + + - alert: ObjectOSAuthFailureSpike + expr: rate(auth_failures_total[5m]) > 5 + for: 10m + annotations: + summary: "Sustained auth failure rate — check for misconfiguration or attack" + + - alert: ObjectOSKernelColdStartHigh + expr: rate(kernel_cache_misses_total[15m]) > 1 + for: 15m + annotations: + summary: "Frequent project kernel cold starts — consider raising OS_KERNEL_CACHE_SIZE" +``` + +OpenTelemetry의 경우 `OTEL_EXPORTER_OTLP_ENDPOINT`를 설정하면 ObjectOS가 OTLP +형식으로 트레이스와 메트릭을 내보냅니다. 스팬의 형태는 +[요청 흐름](/docs/architecture#how-a-request-is-served)과 일치합니다. + +## 감사 로그 + +감사 기능이 활성화되면 ObjectOS는 감사 레코드를 `sys_audit_log`에 기록합니다. + +감사 로그는 다음 용도로 사용합니다. + +- 권한에 민감한 변경; +- 설정 변경; +- 사용자/세션 조사; +- 통합 활동; +- 거부된 접근 분석. + +규제 환경에서는 데이터베이스 또는 스토리지 계층에서 감사 테이블을 추가 전용(append-only)으로 +만들고 보존 정책을 정의하십시오. + +## Console 진단 + +Console은 다음과 같은 운영 화면을 노출합니다. + +- Sessions; +- Audit Logs; +- Notifications; +- 웹훅이 활성화된 경우 Webhook Deliveries; +- 시스템 및 보안 개요 대시보드. + +이는 개발자뿐 아니라 운영자와 지원 엔지니어를 위한 것입니다. diff --git a/content/docs/operate/production.ko.mdx b/content/docs/operate/production.ko.mdx new file mode 100644 index 0000000..e1c51bd --- /dev/null +++ b/content/docs/operate/production.ko.mdx @@ -0,0 +1,82 @@ +--- +title: 프로덕션 준비 +description: ObjectOS를 프로덕션에서 안전하게 운영하기 위한 체크리스트. +--- + +# 프로덕션 준비 + +ObjectOS를 프로덕션 트래픽에 노출하기 전에 이 체크리스트를 사용하세요. + +## HTTP 강화 + +ObjectStack 런타임은 디스패처 라우트에 대해 보수적인 보안 헤더를 제공합니다. +프로덕션 배포 시 다음을 확인해야 합니다. + +- `Content-Security-Policy`; +- `X-Content-Type-Options`; +- `X-Frame-Options`; +- `Referrer-Policy`; +- `Permissions-Policy`; +- `Cross-Origin-Resource-Policy`; +- TLS가 확인된 후의 HSTS. + +리버스 프록시가 헤더를 소유하는 경우 다음으로 최종 응답을 확인하세요. + +```bash +curl -I https://app.example.com +``` + +## 시크릿 + +다음 항목은 시크릿 관리자에 저장하세요. + +| 시크릿 | 용도 | +|---|---| +| `OS_AUTH_SECRET` | 세션 서명 기본 시크릿 | +| `OS_CLOUD_API_KEY` | 컨트롤 플레인 Artifact API 접근 | +| 데이터베이스 자격 증명 | 비즈니스 데이터베이스 접근 | +| OIDC 클라이언트 시크릿 | 엔터프라이즈 SSO | +| 프로바이더 API 키 | 이메일, 스토리지, AI, 통합 | + +시크릿을 아티팩트나 이미지에 절대 포함하지 마세요. + +## 속도 제한 + +프레임워크는 토큰 버킷 속도 제한기를 제공합니다. 호출자 IP와 인증된 +신원을 신뢰할 수 있는 어댑터, 인그레스 또는 게이트웨이 계층에서 속도 제한을 +구성하세요. + +권장 버킷: + +| 트래픽 | 예시 제한 | +|---|---:| +| 인증 엔드포인트 | 10/min/IP | +| 쓰기 요청 | 60/min/IP | +| 읽기 요청 | 600/min/IP | + +다중 파드 배포에는 Redis와 같은 공유 백엔드를 사용하세요. + +## CORS + +명시적인 오리진을 구성하세요. + +```text +https://app.example.com +https://admin.example.com +``` + +자격 증명이 포함된 요청에는 와일드카드 오리진을 사용하지 마세요. + +## 출시 체크리스트 + +- [ ] TLS가 엣지 또는 인그레스에서 종료됩니다. +- [ ] 보안 헤더가 존재합니다. +- [ ] TLS 검증 후 HSTS가 활성화되어 있습니다. +- [ ] CORS 오리진이 명시적입니다. +- [ ] 속도 제한이 인증 및 쓰기 엔드포인트를 보호합니다. +- [ ] `OS_AUTH_SECRET`이 강력하며 시크릿으로 저장되어 있습니다. +- [ ] OIDC 콜백 URL이 공개 도메인과 일치합니다. +- [ ] 비즈니스 데이터베이스 백업 및 복원이 테스트되었습니다. +- [ ] 감사 로그가 고객 정책에 따라 보존됩니다. +- [ ] 조직 간 부정 접근 테스트가 통과합니다. +- [ ] 롤백 계획이 ObjectOS 이미지와 아티팩트 버전을 모두 포함합니다. diff --git a/content/docs/operate/troubleshooting.ko.mdx b/content/docs/operate/troubleshooting.ko.mdx new file mode 100644 index 0000000..019a350 --- /dev/null +++ b/content/docs/operate/troubleshooting.ko.mdx @@ -0,0 +1,94 @@ +--- +title: 문제 해결 +description: 시작, 아티팩트, 인증, 권한, 배포 문제를 진단합니다. +--- + +# 문제 해결 + +증상에서 시작한 다음, 가장 작은 경계부터 먼저 확인하세요. + +## ObjectOS가 시작되지 않음 + +확인 사항: + +1. 컨테이너 로그. +2. Node 버전 및 패키지 설치. +3. `PORT` 충돌. +4. 누락된 아티팩트. +5. 인증 엔드포인트가 필요할 때 누락된 `OS_AUTH_SECRET`. + +Docker의 경우: + +```bash +docker compose -f docker/docker-compose.yml logs objectos +``` + +## 아티팩트를 로드할 수 없음 + +확인 사항: + +- `OS_ARTIFACT_FILE`이 마운트된 파일을 가리키는지; +- 파일이 컨테이너 내부에 존재하는지; +- 파일이 유효한 JSON인지; +- 아티팩트가 소스 메타데이터가 아니라 컴파일된 ObjectStack 아티팩트인지; +- 파일 권한이 읽기 액세스를 허용하는지. + +컨테이너 내부에서: + +```bash +ls -l /artifacts/objectstack.json +``` + +## 로그인 실패 + +확인 사항: + +- `OS_AUTH_SECRET`이 구성되어 있는지; +- 공개 URL과 콜백 URL이 일치하는지; +- OIDC 디스커버리 URL에 ObjectOS에서 접근할 수 있는지; +- 신뢰할 수 있는 오리진에 공개 도메인이 포함되어 있는지; +- 쿠키가 올바른 프로젝트 호스트 이름으로 범위가 지정되어 있는지; +- 프로젝트 커널에서 인증이 활성화되어 있는지. + +## 사용자가 레코드를 볼 수 없음 + +확인 사항: + +1. 올바른 프로젝트 호스트 이름. +2. 사용자가 예상된 조직에 속해 있음. +3. 객체 `read` 권한. +4. 행 수준 보안. +5. 공유 규칙 또는 레코드 공유. +6. 일부 필드만 누락된 경우 필드 보안. + +## 설정을 편집할 수 없음 + +설정이 환경 재정의에 의해 잠겨 있을 수 있습니다. 유효 설정은 +다음 순서로 해석됩니다: + +```text +Environment -> Tenant -> User -> Default +``` + +환경에서 값을 제공하면, 런타임 편집은 값을 자동으로 덮어쓰는 대신 +거부되어야 합니다. + +## 웹훅 또는 작업이 실행되지 않음 + +확인 사항: + +- 아티팩트의 `requires` 목록에 필요한 기능이 포함되어 있는지; +- ObjectOS 이미지에 선택적 서비스 패키지가 포함되어 있는지; +- 큐/작업 서비스 구성을 사용할 수 있는지; +- 대상으로의 아웃바운드 네트워크 액세스가 허용되는지; +- 전달 로그 또는 작업 실행이 Console 진단에서 표시되는지. + +## 데이터베이스 오류 + +확인 사항: + +- 데이터베이스 URL 및 드라이버 유형; +- ObjectOS에서 데이터베이스로의 네트워크 액세스; +- 자격 증명 및 TLS 옵션; +- 스키마 동기화/마이그레이션 로그; +- 로컬 SQLite를 사용하는 경우 스토리지 지속성. diff --git a/content/docs/operate/upgrade.ko.mdx b/content/docs/operate/upgrade.ko.mdx new file mode 100644 index 0000000..39bd597 --- /dev/null +++ b/content/docs/operate/upgrade.ko.mdx @@ -0,0 +1,66 @@ +--- +title: 업그레이드 및 롤백 +description: ObjectOS와 애플리케이션 아티팩트를 안전하게 업그레이드하세요. +--- + +# 업그레이드 및 롤백 + +ObjectOS에는 두 가지 버전 스트림이 있습니다. + +| 버전 | 소유자 | 롤백 | +|---|---|---| +| ObjectOS 이미지/런타임 | 플랫폼/런타임 팀 | 이전 컨테이너 태그 | +| 애플리케이션 아티팩트 | 애플리케이션/컨트롤 플레인 릴리스 | 이전 아티팩트 또는 프로젝트 버전 포인터 | + +아티팩트를 제자리에서 변경하지 마세요. 새 아티팩트를 게시하고 런타임을 해당 +아티팩트로 전환하세요. + +## ObjectOS 업그레이드 + +Docker Compose의 경우: + +```bash +docker compose -f docker/docker-compose.yml pull +docker compose -f docker/docker-compose.yml up -d +``` + +Kubernetes의 경우, 이미지 태그를 업데이트하고 배포가 롤링되도록 하세요. + +## 아티팩트 업그레이드 + +파일 기반 모드: + +```bash +cp objectstack-2026-05-24.json docker/artifacts/objectstack.json +docker compose -f docker/docker-compose.yml restart objectos +``` + +클라우드 연결 모드: + +1. 새 아티팩트를 컨트롤 플레인에 게시합니다. +2. 현재 프로젝트/환경 포인터를 새 버전으로 이동합니다. +3. 캐시 만료 후 ObjectOS가 다시 가져오도록 하거나 재시작하여 강제로 다시 로드합니다. + +## 롤백 + +ObjectOS 롤백: Compose 파일(또는 배포 매니페스트)에서 이전 이미지 태그를 고정한 +다음 다시 적용하세요. + +```bash +docker compose -f docker/docker-compose.yml up -d objectos +``` + +아티팩트 롤백: + +- 이전에 마운트된 파일을 복원하거나, +- 컨트롤 플레인 포인터를 이전 아티팩트 버전으로 되돌립니다. + +## 호환성 점검 + +업그레이드 전에: + +- 아티팩트가 호환되는 ObjectStack 버전으로 빌드되었는지 확인합니다. +- 필요한 기능이 ObjectOS 이미지에서 사용 가능한지 확인합니다. +- 데이터베이스 마이그레이션 또는 스키마 동기화 동작을 이해하고 있는지 확인합니다. +- 인증 및 권한 스모크 테스트를 실행합니다. +- 롤백이 파괴적인 데이터 변경을 요구하지 않는지 확인합니다. diff --git a/content/docs/quickstart.ko.mdx b/content/docs/quickstart.ko.mdx new file mode 100644 index 0000000..d084010 --- /dev/null +++ b/content/docs/quickstart.ko.mdx @@ -0,0 +1,232 @@ +--- +title: 빠른 시작 +description: 처음부터 실행 중인 ObjectOS까지 — CLI 하나를 설치하고 명령 하나를 실행하면 앱이 완성됩니다. +--- + +# 빠른 시작 + +무엇을 하려는지에 따라 시작하는 방법이 두 가지 있습니다. + +| 당신은 … | 여기서 시작하세요 | +|---|---| +| ObjectOS를 처음 사용해 보거나 프로덕션에서 실행하는 경우 | [경로 A — `os start`](#path-a--os-start-operator-first-time-evaluator) | +| 코드로 앱을 빌드하거나 커스터마이징하는 경우 | [경로 B — `os init`](#path-b--os-init-developer) | + +두 경로 모두 Console + Account가 포함된 실행 중인 서버를 생성합니다. +차이점은 소스 파일을 스캐폴딩하는지 여부입니다. + +## 사전 요구 사항 + +- **Node.js 20 이상** — `node --version` +- 터미널 + +이것이 전부입니다. Docker 불필요. 데이터베이스 불필요. 계정 가입 불필요. + +--- + +## 경로 A — `os start` (운영자 / 처음 평가하는 사용자) + +CLI를 전역으로 설치한 다음 실행합니다: + +```bash +npm i -g @objectstack/cli +os start +``` + +다음과 같은 화면이 표시됩니다: + +```text +◆ ObjectStack +──────────────────────────────────────── +🏠 Home: ~/.objectstack +📦 Artifact: none (empty kernel — install apps via Console marketplace) +🗄️ Database: file:~/.objectstack/data/objectstack.db + + ✓ Server is ready + + ➜ API: http://localhost:3000/ + ➜ Console: http://localhost:3000/_console/ + ➜ Account: http://localhost:3000/_account/ + ➜ Console: http://localhost:3000/_console/ + + Plugins: 23 loaded +``` + +이것이 전부입니다. ObjectOS가 실행되고 있습니다. + +### 실행 중인 항목 + +| URL | 무엇인지 | +|---|---| +| http://localhost:3000/_account/register | 첫 번째 계정 생성 | +| http://localhost:3000/_console/ | 관리자 UI — 그리고 **앱 marketplace** | +| http://localhost:3000/_console/ | 사용자, 역할, 감사 로그, 설정 | +| http://localhost:3000/health | 활성 상태 프로브 | + +런타임은 **빈 커널**로 부팅되며 — 오브젝트도 앱도 없습니다 — +marketplace를 노출하여 몇 초 만에 완성된 앱을 설치할 수 있게 합니다. + +### 채팅으로 빌드 — AI Builder + +로그인한 후, Console에서 AI 어시스턴트(오른쪽 상단의 +반짝이 아이콘)를 열고 필요한 것을 설명하세요: + +> *"고객 지원 티켓을 추적해야 합니다. 각 티켓에는 제목, +> 설명, 우선순위(낮음/중간/높음/긴급), 상태, 담당자가 +> 있습니다. 상태별로 그룹화된 칸반 뷰를 추가하세요."* + +AI가 계획을 제안하고, 당신이 승인하면, 메타데이터가 바로 적용됩니다 — +REST 엔드포인트, Console 뷰, 감사 로그 항목, 권한 게이트까지. +파일 편집도, 재시작도 필요 없습니다. 전체 어휘는 [Build → AI Builder](/docs/build/ai-builder)를 +참고하세요. + +> **IDE에서 직접 코딩하시나요?** `npx skills add objectstack-ai/framework`를 +> 실행하여 Claude Code / Cursor / Copilot / Codex가 실제 Zod 스키마에 맞춰 +> ObjectOS 메타데이터를 작성하는 방법을 익히도록 하세요. +> [Build → IDE Skills](/docs/build/ai-skills)를 참고하세요. + +### marketplace에서 앱 설치 + +**http://localhost:3000/_console/**를 열고, 로그인한 후, 앱을 선택하세요: + +| 앱 | 제공하는 기능 | +|---|---| +| Todo | 범용 작업 및 프로젝트 추적기 | +| Contracts | AI 추출이 포함된 계약 라이프사이클 | +| Procurement | 공급업체, 발주서, 3자 매칭 | +| Compliance | SOC 2 / ISO 27001 통제 항목 + 증빙 | +| Helpdesk | AI 우선 고객 지원 | +| Content | 편집 캘린더 + 채널 ROI | +| HR | 디렉터리, 조직도, 휴가 | + +설치 → 새로고침 → 오브젝트, 뷰, 권한, 시드 데이터와 함께 +바로 사용할 수 있습니다. 재시작이 필요 없습니다. + +### 자주 쓰는 플래그 + +```bash +os start --port 3200 # different port +os start --database postgres://... # external database +os start --auth-secret "$(openssl rand -hex 32)" # enable auth in /api/v1/auth/* +os start --home /var/lib/objectos # persistent home (production) +``` + +모든 옵션은 [Runtime Configuration](/docs/configure/runtime)을, +프로덕션 형태의 경로는 [Docker](/docs/deploy/docker)를 참고하세요. + +--- + +## 경로 B — `os init` (개발자) + +직접 데이터 모델, 뷰, 플로우를 정의하기 위해 TypeScript를 작성할 때 +이 경로를 사용하세요. + +```bash +npx @objectstack/cli init my-app -t app --install +cd my-app +pnpm dev +``` + +다음과 같은 화면이 표시됩니다: + +```text +✓ Project initialized! + +◆ Compile + ✓ Build complete (462ms) + Data: 1 Objects 3 Fields + +◆ Development Mode + ✓ Server is ready + + ➜ API: http://localhost:3002/ + ➜ Console: http://localhost:3002/_console/ + ➜ Account: http://localhost:3002/_account/ + ➜ Console: http://localhost:3002/_console/ +``` + +dev 서버는 3000에서 실행 중인 `os start`와 충돌을 피하기 위해 +**3002** 포트를 사용한다는 점에 유의하세요. + +### 직접 오브젝트 추가하기 + +`src/objects/task.ts`를 편집하세요: + +```ts +// src/objects/task.ts +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Task = ObjectSchema.create({ + name: 'task', + label: 'Task', + fields: { + subject: Field.text({ label: 'Subject', required: true, maxLength: 200 }), + done: Field.boolean({ label: 'Done', defaultValue: false }), + due: Field.date({ label: 'Due' }), + assignee: Field.lookup({ label: 'Assignee', reference: 'sys_user' }), + }, +}); +``` + +저장하세요. dev 서버가 다시 컴파일되고 즉시 다음을 사용할 수 있습니다: + +- **`/api/v1/data/task`** — 필터/정렬/페이지네이션이 포함된 전체 CRUD +- **Console의 "Task" 뷰** — 목록, 폼, 상세가 모두 생성됨 +- **Console의 권한 행** — 역할별로 읽기/쓰기 권한 부여 +- **감사 로그 항목** — 모든 생성/수정/삭제가 기록됨 + +마이그레이션 없음. 코드 생성 없음. 재시작 없음. + +### 프로젝트 레이아웃 + +```text +my-app/ +├── objectstack.config.ts # Stack definition (manifest + objects) +├── src/ +│ └── objects/ # Your data model — add files here +├── dist/ +│ └── objectstack.json # Compiled artifact (regenerated on save) +├── package.json +└── tsconfig.json +``` + +`dist/objectstack.json`은 프로덕션에 배포하는 파일입니다 — 실행 중인 +ObjectOS 컨테이너에 마운트하면 그것이 당신의 앱이 됩니다. + +### 또는 템플릿에서 시작하기 + +프로덕션 형태의 스타터는 +[github.com/objectstack-ai/templates](https://github.com/objectstack-ai/templates)에 있습니다: + +```bash +git clone https://github.com/objectstack-ai/templates.git +cd templates/packages/todo +pnpm install +pnpm dev # http://localhost:4002 +``` + +각 템플릿은 2500 LOC 미만으로, 한 번에 읽을 수 있으며, 독립적으로 실행됩니다. + +--- + +## 기본으로 로드되는 항목 + +어느 경로든 다음 23개의 플러그인을 자동으로 제공합니다: + +> Auth, Security (RBAC + RLS + FLS), Audit, REST API, Console UI, +> Account UI, Console UI, AI Service, Queue, Jobs, Cache, Settings, +> Email, Storage, Marketplace, Metadata, ObjectQL, 그리고 SQL 드라이버. + +이들 중 어느 것도 직접 import하거나 연결할 필요가 없습니다 — +무언가가 필요하다고 선언하면 자동으로 활성화됩니다. + +## 다음 단계 + +| 이제 무엇을 | 읽어보기 | +|---|---| +| Docker에서 실행 (프로덕션 형태) | [Docker](/docs/deploy/docker) | +| SQLite 대신 Postgres 사용 | [Runtime Configuration](/docs/configure/runtime) | +| Google / Okta / Entra 로그인 추가 | [Authentication](/docs/configure/authentication) | +| 누가 무엇을 할 수 있는지 제한 | [Permissions](/docs/configure/permissions) | +| Slack / Zapier / 내 서비스로 이벤트 전송 | [Webhooks](/docs/configure/webhooks) | +| 프로덕션에 배포 | [Production Readiness](/docs/operate/production) | diff --git a/content/docs/reference/cel.ko.mdx b/content/docs/reference/cel.ko.mdx new file mode 100644 index 0000000..4549657 --- /dev/null +++ b/content/docs/reference/cel.ko.mdx @@ -0,0 +1,162 @@ +--- +title: CEL 표현식 +description: 수식, 술어, 스케줄, 템플릿 문자열에 사용되는 표현식 언어 — 다섯 가지 태그드 템플릿으로 제공됩니다. +--- + +# CEL 표현식 + +ObjectOS는 작고 안전하며 샌드박스화된 표현식이 필요한 모든 곳에서 +[CEL](https://github.com/google/cel-spec)(Common Expression Language)을 +사용합니다. 수식 필드, 검증 규칙, 가시성 술어, 공유 조건, 플로우 가드, +스케줄, 템플릿 문자열 등이 여기에 해당합니다. + +작성은 `@objectstack/spec`에서 가져오는 **다섯 가지 태그드 템플릿**을 통해 +이루어집니다. 이들은 모두 런타임이 파싱하는 작은 JSON 객체를 생성합니다: + +```ts +{ dialect: 'cel' | 'template' | 'cron', source: string } +``` + +스키마 소스: +[`packages/spec/src/shared/expression.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/shared/expression.zod.ts). + +## 다섯 가지 태그드 템플릿 + +| 템플릿 | Dialect | 용도 | 예시 | +|:--|:--|:--|:--| +| `` F`...` `` | `cel` | **수식 필드** — 레코드와 함께 저장되는 파생 값 | `` F`record.amount * 0.1` `` | +| `` P`...` `` | `cel` | **술어** — 검증 / 공유 / 가시성 / 조건을 위한 불리언 | `` P`record.status == "open"` `` | +| `` cel`...` `` | `cel` | **일반 CEL** — F나 P가 적합하지 않을 때(예: 파라미터 값) | `` cel`now() + duration("P30D")` `` | +| `` tmpl`...` `` | `template` | `{{var}}` 보간을 사용하는 **문자열 템플릿** | `` tmpl`Order from {{record.customer.name}}` `` | +| `` cron`...` `` | `cron` | **스케줄** — 표준 5필드 cron 구문 | `` cron`0 9 * * 1-5` `` | + +평가 시점에서 `F`, `P`, `cel` 사이에는 기능적 차이가 없습니다 — 모두 CEL을 +실행합니다. 이렇게 나눈 이유는 스키마(및 AI 에이전트)가 표현식이 담당하는 +역할을 알 수 있도록 하고, 에디터가 타입 검사를 할 수 있도록 하기 +위함입니다(수식은 값을 반환해야 하고, 술어는 불리언을 반환해야 합니다). + +### Import + +```ts +import { F, P, cel, tmpl, cron } from '@objectstack/spec' +``` + +## 각각의 사용 위치 + +| 스펙의 필드 | 태그 | 예시 위치 | +|:--|:--|:--| +| `Field.expression` (formula 타입) | `F` | `*.object.ts` 수식 필드 | +| `Field.conditionalRequired` | `P` | object 필드 | +| `Validation.predicate` | `P` | object 검증 | +| `SharingRule.condition` | `P` | 공유 규칙 | +| `View.conditionalFormatting[].condition` | `P` | 뷰 | +| `Flow.step.when` / `Flow.transition.when` | `P` | 플로우 | +| `Action.guard` | `P` | 액션 | +| 알림 제목 / 메시지 본문 | `tmpl` | 알림 | +| `Schedule.cron` | `cron` | 예약된 플로우 / 리포트 | +| 임의의 값 파라미터 | `cel` | 플로우 단계 입력 | + +## 변수 스코프 + +CEL 표현식은 다음과 같은 최상위 변수를 가진 컨텍스트에서 평가됩니다: + +| 변수 | 사용 가능한 경우 | 내용 | +|:--|:--|:--| +| `record` | 거의 항상 | 현재 평가 중인 레코드 | +| `previous` | 업데이트 훅 / 변경 감지 시 | 레코드의 변경 전 상태(또는 `null`) | +| `input` | 액션, 플로우 단계 | 사용자가 제공한 입력 페이로드 | +| `os.user` | 항상 | `{ id, roles: string[], permissions: string[] }` | +| `os.org` | 항상 | 조직 / 테넌트 컨텍스트 | +| `os.env` | 항상 | 표현식에 노출되는 환경 변수 | + +> 레거시 `OLD` / `NEW` 변수는 M9.5에서 제거되었습니다. `previous`와 +> `record`를 사용하세요. + +## 표준 라이브러리 + +[`packages/formula/src/stdlib.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/formula/src/stdlib.ts)에 +등록되어 있습니다. 가장 많이 사용되는 빌트인: + +### 시간 + +| 함수 | 반환 | 비고 | +|:--|:--|:--| +| `now()` | `Timestamp` | 평가 컨텍스트에 고정됨 — 단일 쿼리 내에서 안정적 | +| `today()` | `Timestamp` | UTC 날짜의 시작 | +| `daysFromNow(int)` | `Timestamp` | 미래 날짜 | +| `daysAgo(int)` | `Timestamp` | 과거 날짜 | + +CEL은 네이티브 `timestamp(...)`, `duration(...)`, +`date.getDayOfWeek()` 등도 포함합니다 — +[CEL 스펙](https://github.com/google/cel-spec/blob/master/doc/langdef.md#standard-definitions)을 +참고하세요. + +### 유틸리티 + +| 함수 | 목적 | +|:--|:--| +| `isBlank(x)` | `null`, `undefined`, `""`, 또는 빈 리스트이면 `true` | +| `coalesce(a, b)` | 첫 번째 non-null 값 | +| `trim(s)` | 공백 제거 | +| `joinNonEmpty(list, sep)` | 비어 있지 않은 항목들을 연결 | + +네이티브 CEL 문자열 헬퍼(`.contains(...)`, `.startsWith(...)`, +`.matches(...)`, `.size()`)는 항상 사용할 수 있습니다. + +## 예시 + +**수식 필드** — 라인 항목 합계: + +```ts +{ name: 'subtotal', type: 'formula', expression: F`record.quantity * record.unit_price` } +``` + +**검증** — 마감일은 오늘 이후여야 함: + +```ts +{ message: 'Close date must be in the future', predicate: P`record.close_date > today()` } +``` + +**가시성** — 매니저에게만 필드 표시: + +```ts +{ visibleIf: P`'manager' in os.user.roles` } +``` + +**플로우 가드** — 금액이 작을 때 단계 건너뛰기: + +```ts +{ when: P`record.amount >= 1000` } +``` + +**스케줄** — 평일 오전 9시: + +```ts +{ schedule: cron`0 9 * * 1-5` } +``` + +**템플릿** — 알림 제목: + +```ts +{ subject: tmpl`[{{record.priority}}] {{record.subject}}` } +``` + +## 오류 + +표현식은 로드 시점에 컴파일됩니다. 실패는 소스 위치와 함께 +`VALIDATION_ERROR`로 나타납니다: + +```json +{ "code": "VALIDATION_ERROR", "message": "CEL: unknown field 'amout' on Record", "details": { "field": "subtotal", "expression": "record.amout * 0.1" } } +``` + +런타임에서 평가가 실패하면 수식 필드의 경우 `null`로, 술어의 경우 +`false`로 처리되며, 감사 로그에 항목이 추가됩니다. + +## 참고 + +- [필드 타입](./field-types) — 수식 및 조건부 필수 필드 +- [Build → 데이터 모델](/docs/build/data-model) — 검증 및 술어 +- [Build → 플로우](/docs/build/flows) — 가드 및 스케줄 +- [`@objectstack/spec/shared/expression.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/shared/expression.zod.ts) — 스키마 +- [`@objectstack/formula/stdlib.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/formula/src/stdlib.ts) — 빌트인 diff --git a/content/docs/reference/cli.ko.mdx b/content/docs/reference/cli.ko.mdx new file mode 100644 index 0000000..b8c9fb1 --- /dev/null +++ b/content/docs/reference/cli.ko.mdx @@ -0,0 +1,209 @@ +--- +title: CLI 레퍼런스 +description: 모든 `os` 명령어와 그 기능, 그리고 가장 유용한 플래그. +--- + +# CLI 레퍼런스 + +`@objectstack/cli` 패키지는 단일 바이너리 `os`(별칭 `objectstack`로도 +사용 가능)를 설치합니다. 아래의 모든 예제는 더 짧은 `os`를 사용합니다. + +```bash +npm i -g @objectstack/cli +os --help +``` + +## 서버 명령어 + +### `os start` — 설정 없는 부팅 + +4가지 단계적 모드를 자동으로 감지합니다: + +1. **빈 부팅(Empty boot)** — cwd에 아티팩트도 설정도 없음 → Console과 + marketplace가 포함된 최소한의 커널을 부팅합니다. +2. **프로젝트 부팅(Project boot)** — cwd에 `objectstack.config.ts`가 있음 → + `./dist/objectstack.json`으로 자동 컴파일한 뒤 그것으로 부팅합니다. +3. **아티팩트 부팅(Artifact boot)** — `dist/objectstack.json`에 접근 가능 → + 그것으로 직접 부팅합니다. +4. **명시적 재정의(Explicit overrides)** — `--artifact`, `--database`, + `--port`가 우선합니다. + +```bash +os start # default port 3000 +os start --port 8080 +os start --artifact ./build/myapp.json +os start --artifact https://cdn.example.com/app.json +os start --database file:./data/prod.db +os start --database postgres://user:pass@host:5432/mydb +os start --database libsql://my-db.turso.io --database-auth-token $TURSO +os start --auth-secret "$(openssl rand -hex 32)" +os start --home /var/lib/objectos # persistent home dir +os start --no-ui # API only (no Console/Account) +``` + +HOME 디렉터리 기본값: +- cwd에 프로젝트 설정이 있을 때 → `/.objectstack` (프로젝트 로컬) +- 없을 때 → `~/.objectstack` (전역, 호출 간 공유) + +### `os serve` — 프로덕션 서버 + +`objectstack.config.ts`가 있으면 이를 읽고, 없으면 `dist/objectstack.json` +(또는 `http(s)://` URL을 포함한 `OS_ARTIFACT_PATH`)으로 대체합니다. +엄격한 동작을 원하는 프로덕션 컨테이너에서 사용하세요. + +```bash +os serve # port 3000 +os serve --port 4000 --no-ui +``` + +### `os dev` — 핫 리로드를 지원하는 개발 + +`objectstack.config.ts`와 `src/`를 감시합니다. 저장 시 재컴파일 및 +리로드합니다. 스캐폴딩된 프로젝트에서 npm `dev` 스크립트로 사용됩니다. + +```bash +os dev # port 3002 by default +os dev -p 4002 +``` + +### `os studio` — 개발 서버가 포함된 Console + +`os dev`와 동일하지만 Console이 명시적으로 활성화됩니다. + +## 프로젝트 명령어 + +### `os init` — 새 프로젝트 스캐폴딩 + +```bash +os init my-app # interactive +os init my-app -t app --install # auto-install with default pnpm +os init my-app -t app --install -p npm +os init my-app -t plugin # plugin scaffold +os init my-app -t empty # bare minimum +``` + +템플릿: +| 템플릿 | 제공하는 것 | +|---|---| +| `app` | 전체 앱 — 오브젝트, 뷰, 액션을 갖추고 확장할 준비 완료 | +| `plugin` | 재사용 가능한 기능을 배포하기 위한 플러그인 스캐폴드 | +| `empty` | 매니페스트와 tsconfig만 | + +### `os create` — 템플릿에서 패키지, 플러그인 또는 예제 생성 + +```bash +os create plugin my-plugin +os create example my-example +``` + +## 빌드 / 검증 명령어 + +| 명령어 | 목적 | +|---|---| +| `os compile` (`os build`) | `objectstack.config.ts` → `dist/objectstack.json` 컴파일 | +| `os validate` | 프로토콜 스키마에 대해 설정을 검증 | +| `os lint` | 스타일 및 규칙 검사 (CI에서 권장) | +| `os diff ` | 두 설정을 비교하여 호환성 깨짐(breaking changes) 감지 | +| `os info` | 설정 또는 아티팩트의 메타데이터 요약 출력 | +| `os explain ` | 오브젝트 스키마에 대한 사람이 읽기 쉬운 설명 | +| `os doctor` | 상태 점검: 순환 의존성, 깨진 참조, 환경 문제 감지 | +| `os generate` (`os g`) | TypeScript 타입 / 메타데이터 파일 생성 | + +`os doctor`는 **무언가 이상하다고 느껴질 때 가장 먼저 실행해야 할 명령어**입니다. +런타임이 런타임 경고로만 드러낼 잘못된 설정을 잡아냅니다. + +## 데이터 명령어 + +터미널에서 레코드를 조작합니다 — 시딩, 마이그레이션, CI 스모크 테스트에 +유용합니다. + +```bash +os data create --data '{"subject": "Hello"}' +os data get +os data query --filter 'status:active' --limit 10 +os data update --data '{"done": true}' +os data delete +``` + +## 메타데이터 명령어 + +런타임에 메타데이터 레코드(오브젝트, 뷰, 권한 세트 등)를 읽고 씁니다. + +```bash +os meta list +os meta get +os meta register +os meta delete +``` + +## i18n 명령어 + +```bash +os i18n extract # extract translation keys from source +os i18n check # find missing keys across configured locales +``` + +## 클라우드 명령어 + +ObjectStack Cloud(선택적 호스팅 컨트롤 플레인) 사용자를 위한 명령어입니다. + +```bash +os login # interactive +os logout +os whoami +os register # create an account +os cloud login | logout | whoami +``` + +## 게시 / 패키지 명령어 + +```bash +os package publish # publish artifact as a versioned package +os publish # publish to ObjectStack Cloud +os rollback # activate a previous artifact revision +``` + +## 환경 명령어 + +컨트롤 플레인을 기반으로 프로젝트당 여러 환경(dev, staging, prod)을 +사용하는 배포를 위한 명령어입니다. + +```bash +os environments list +os environments show +os environments create +os environments switch +os environments bind # bind local artifact to an environment +``` + +## 테스트 명령어 + +```bash +os test # run Quality Protocol scenarios against a running server +``` + +## 공통 플래그 규칙 + +| 플래그 | 의미 | +|---|---| +| `-p, --port` | HTTP 포트 | +| `-a, --artifact` | 컴파일된 아티팩트의 경로 또는 URL | +| `-d, --database` | 데이터베이스 URL | +| `--home` | 영구 상태 디렉터리 | +| `-v, --verbose` | 상세 출력 | +| `--no-ui` | Console / Account 포털 비활성화 | + +전체 플래그 목록을 보려면 어떤 명령어든 `--help`와 함께 실행하세요: + +```bash +os start --help +os data query --help +``` + +## CLI의 위치 + +- **운영자 / 첫 실행** → `os start` +- **앱을 개발하는 개발자** → `os init` → `os dev` → `os compile` +- **CI / 릴리스 파이프라인** → `os lint && os validate && os compile && os test` +- **프로덕션 런타임** → `os serve` (일반적으로 Docker 이미지의 `CMD` 내부) +- **진단** → `os doctor`, `os info`, `os explain` diff --git a/content/docs/reference/environment-variables.ko.mdx b/content/docs/reference/environment-variables.ko.mdx new file mode 100644 index 0000000..b7b54f3 --- /dev/null +++ b/content/docs/reference/environment-variables.ko.mdx @@ -0,0 +1,87 @@ +--- +title: 환경 변수 +description: ObjectOS 런타임 환경 변수 참조. +--- + +# 환경 변수 + +배포 수준 구성 및 시크릿에는 환경 변수를 사용하세요. +테넌트/사용자가 편집 가능한 애플리케이션 구성에는 시스템 설정을 사용하세요. + +> **명명 규칙.** 다음 릴리스부터 모든 ObjectStack 소유 변수는 +> `OS_` 접두사를 사용합니다. 1.0 이전의 접두사 없는 이름(`PORT`, `AUTH_SECRET`, `OS_MULTI_TENANT`, +> …)도 여전히 동작하지만 일회성 사용 중단 경고를 출력합니다. 새 배포에서는 표준 +> `OS_*` 이름을 사용하는 것이 좋습니다. [레거시 별칭](#legacy-aliases)을 참조하세요. + +## 핵심 + +| 변수 | 필수 | 설명 | +|---|---:|---| +| `OS_PORT` | 아니오 | 런타임이 수신 대기하는 HTTP 포트. 기본값은 `3000`. 레거시 별칭: `PORT`. | +| `OS_AUTH_SECRET` | 인증 시 필수 | 프로젝트별 인증 시크릿을 파생하는 데 사용되는 기본 시크릿. 레거시 별칭: `AUTH_SECRET`. | + +## 아티팩트 및 프로젝트 확인 + +| 변수 | 필수 | 설명 | +|---|---:|---| +| `OS_ARTIFACT_FILE` | 파일 모드 | 로컬에서 컴파일된 `objectstack.json`의 경로. ObjectOS 앱 래퍼가 읽어 `fileConfig.artifactPath`로 전달합니다. | +| `OS_ARTIFACT_PATH` | 대안 | 동일한 경로 또는 `http(s)://` URL에 대한 프레임워크 수준의 이름. `@objectstack/runtime`이 직접 처리합니다(CLI `dev`/`start`, 독립 실행형 모드). | +| `OS_PROJECT_ID` | 선택 | `OS_ENVIRONMENT_ID`의 레거시 별칭으로, 하위 호환성을 위해 ObjectOS 래퍼에서 허용됩니다. 새 배포에서는 `OS_ENVIRONMENT_ID`를 사용하는 것이 좋습니다. | +| `OS_ENVIRONMENT_ID` | 선택 | 독립 실행형/파일 기반 모드의 환경 id(기본값 `proj_local`). 프로젝트별 인증 시크릿을 파생하는 데에도 사용됩니다. ObjectOS 래퍼는 레거시 별칭 `OS_PROJECT_ID`도 허용합니다. | +| `OS_ORGANIZATION_ID` | 선택 | 파일 기반 모드의 기본 조직 id(기본값 `org_local`). | +| `OS_WATCH_ARTIFACT` | 아니오 | 로컬 아티팩트가 변경될 때 다시 로드하려면 `1`로 설정합니다. 개발 환경에서만 사용하세요. | +| `OS_CLOUD_URL` | 클라우드 모드 | 호스트명 확인 및 아티팩트 가져오기를 위한 컨트롤 플레인 기본 URL. 클라우드 연결 동작을 사용하지 않으려면 `off` 또는 `local`로 설정합니다. | +| `OS_CLOUD_API_KEY` | 선택 | 비공개 컨트롤 플레인 Artifact API 접근을 위한 Bearer 토큰. | +| `OS_MULTI_ORG_ENABLED` | 아니오 | 멀티 테넌트 라우팅/조직 전환을 활성화하려면 `true`로 설정합니다(기본값 `false`). 레거시 별칭: `OS_MULTI_TENANT`. | +| `OS_RUNTIME_PORT` | 개발 전용 | `http://localhost:`에서 개발할 때 플랫폼 SSO 콜백 URL을 구성하는 데 사용되는 localhost 포트. | + +## 캐시 + +| 변수 | 기본값 | 설명 | +|---|---:|---| +| `OS_KERNEL_CACHE_SIZE` | `32` | 캐시된 프로젝트 커널의 최대 개수. | +| `OS_KERNEL_TTL_MS` | `900000` | 프로젝트 커널의 유휴 TTL. | +| `OS_ENV_CACHE_TTL_MS` | `300000` | 환경/호스트명 캐시 TTL. | +| `OS_ARTIFACT_CACHE_TTL_MS` | `300000` | 아티팩트 응답 캐시 TTL. | + +## 인증 및 신뢰할 수 있는 출처 + +| 변수 | 설명 | +|---|---| +| `AUTH_SECRET` | `OS_AUTH_SECRET`의 레거시 별칭. 이번 릴리스에서도 여전히 처리되지만 `OS_AUTH_SECRET`을 사용하는 것이 좋습니다. | +| `OS_TRUSTED_ORIGINS` | 쉼표로 구분된 추가 신뢰 출처. | +| `OS_ROOT_DOMAIN` | 플랫폼 SSO 배포에서 프로젝트 하위 도메인을 신뢰하는 데 사용되는 루트 도메인. | +| `OS_PLATFORM_SSO` | 플랫폼 SSO 연결을 비활성화하려면 `false`로 설정합니다. | +| `OS_RUNTIME_PORT` | localhost 프로젝트 호스트명을 위한 로컬 개발 도우미. | + +## 데이터베이스 + +클라우드 연결 모드에서는 컨트롤 플레인이 아티팩트 응답과 함께 +프로젝트별 런타임 데이터베이스 구성을 반환합니다. 파일 기반 +모드에서 ObjectOS는 아티팩트에서 데이터소스 선언을 읽습니다. +최후의 수단으로 프레임워크는 다음도 처리합니다: + +| 변수 | 설명 | +|---|---| +| `OS_DATABASE_URL` | 연결 URL(`file:./db.sqlite`, `libsql://…`, `postgres://…`, `mongodb://…`, `memory://`). 독립 실행형 모드 및 CLI `dev`에서 사용됩니다. | +| `OS_DATABASE_DRIVER` | URL에서 자동 감지된 드라이버를 재정의합니다. | +| `OS_DATABASE_AUTH_TOKEN` | Turso/libSQL과 같은 관리형 드라이버를 위한 인증 토큰. | +| `OS_BUSINESS_DB_URL` | 프로젝트별 비즈니스 데이터베이스 URL에 대한 ObjectOS 래퍼 규칙. 배포에서 이를 `OS_DATABASE_URL` 또는 런타임 데이터소스 재정의로 확인하세요. | +| `OS_CACHE_DIR` | 로컬 아티팩트 및 런타임 캐시 디렉터리(기본값 `/var/cache/objectos`). | +| `OS_SKIP_SCHEMA_SYNC` | 부팅 시 ObjectQL DDL 동기화를 건너뛰려면 `1`로 설정합니다. 스키마를 별도로 관리할 때 사용하세요. | + +ObjectOS 고객 배포의 경우 컨테이너 로컬 기본값에 의존하기보다는 +명시적인 컨트롤 플레인 런타임 구성 또는 아티팩트 데이터소스 구성을 +사용하는 것이 좋습니다. + +## 레거시 별칭 + +이 1.0 이전 이름들은 이번 릴리스에서도 여전히 동작하지만 일회성 사용 중단 +경고를 출력합니다. 향후 메이저 버전에서 제거될 예정입니다. 표준 이름을 사용하는 것이 좋습니다. + +| 표준 | 레거시 | +|---|---| +| `OS_PORT` | `PORT` | +| `OS_AUTH_SECRET` | `AUTH_SECRET` | +| `OS_MULTI_ORG_ENABLED` | `OS_MULTI_TENANT` | +| `OS_ENVIRONMENT_ID` | `OS_PROJECT_ID` | diff --git a/content/docs/reference/field-types.ko.mdx b/content/docs/reference/field-types.ko.mdx new file mode 100644 index 0000000..6889531 --- /dev/null +++ b/content/docs/reference/field-types.ko.mdx @@ -0,0 +1,208 @@ +--- +title: 필드 타입 +description: 객체에 선언할 수 있는 모든 필드 타입 — 저장하는 값, 허용하는 옵션, 그리고 REST, Console, AI Builder에 노출되는 방식. +--- + +# 필드 타입 + +패밀리별로 분류된 53가지 기본 제공 필드 타입. 전체 Zod 스키마는 +[`packages/spec/src/data/field.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/data/field.zod.ts)에 있으며, 이 페이지는 실용적인 요약입니다. + +## 핵심 속성 (모든 필드) + +| 속성 | 타입 | 기본값 | 용도 | +|:--|:--|:--|:--| +| `name` | string (snake_case) | — | 머신 식별자 — REST 경로 세그먼트, SQL 컬럼 | +| `label` | string | — | Console에 표시되는 라벨 | +| `type` | FieldType | — | 아래 타입 표 참조 | +| `required` | boolean | `false` | NOT NULL 제약 | +| `unique` | boolean | `false` | 고유 인덱스 | +| `searchable` | boolean | `false` | `/api/v1/search`용 인덱싱 | +| `multiple` | boolean | `false` | 값의 배열로 저장 | +| `defaultValue` | unknown | — | 초기값 (리터럴 또는 CEL) | +| `columnName` | string | = `name` | 물리적 DB 컬럼 재정의 | +| `hidden` | boolean | `false` | 기본 Console 뷰에서 숨김 | +| `readonly` | boolean | `false` | 폼에서 비활성화 | +| `system` | boolean | `false` | 자동 주입됨 (id, created_at, …) | +| `index` | boolean | `false` | DB 인덱스 생성 | +| `externalId` | boolean | `false` | 외부 키를 통한 upsert 대상 | +| `inlineHelpText` | string | — | 툴팁 / 도움말 텍스트 | +| `conditionalRequired` | `P` 술어 | — | CEL이 true일 때 필수 | +| `auditTrail` | boolean | `false` | 모든 변경을 감사 로그에 기록 | +| `encryptionConfig` | object | — | 필드 단위 암호화 ([Security](./security) 참조) | +| `maskingRule` | string | — | PII 마스킹 패턴 | + +## 텍스트 패밀리 + +| 타입 | 용도 | 주요 옵션 | +|:--|:--|:--| +| `text` | 짧은 문자열 | `maxLength`, `minLength` | +| `textarea` | 여러 줄 | `maxLength` | +| `email` | 이메일 | 형식 검증됨, 소문자로 변환 | +| `url` | URL | 형식 검증됨 | +| `phone` | 전화번호 | E.164 | +| `password` | 시크릿 | 해시 처리됨, GET으로 반환되지 않음 | +| `markdown` | 마크다운 본문 | Console 미리보기에서 렌더링 | +| `html` | 새니타이즈된 HTML | 쓰기 시 DOMPurify 적용 | +| `richtext` | WYSIWYG | Console 에디터 + 직렬화된 JSON | + +## 숫자 + +| 타입 | 용도 | 주요 옵션 | +|:--|:--|:--| +| `number` | 부동소수점 | `min`, `max`, `precision`, `scale` | +| `currency` | 금액 | `currencyConfig: { precision, currencyMode: 'fixed' \| 'dynamic', defaultCurrency }` | +| `percent` | 0–100 % | `min`, `max`, `scale` | + +> `integer`와 `decimal`은 별도의 타입이 아닙니다 — 정수에는 `scale: 0`을 사용한 `number`를, +> 고정 소수에는 `precision`+`scale`을 사용한 `number`를 쓰세요. + +## 날짜 / 시간 + +| 타입 | 저장 내용 | 비고 | +|:--|:--|:--| +| `date` | 달력 날짜 | 시간대 없음 | +| `datetime` | 시점 | UTC | +| `time` | 벽시계 시각 | 날짜 없음 | + +## 논리 + +| 타입 | 비고 | +|:--|:--| +| `boolean` | 체크박스 | +| `toggle` | boolean과 동일, 스위치 UI | + +## 선택 + +| 타입 | 비고 | +|:--|:--| +| `select` | 단일 선택 — 옵션을 인라인으로 선언하거나 피크리스트에서 참조 | +| `multiselect` | 다중 선택, 배열로 저장 | +| `radio` | 라디오 렌더링을 사용하는 `select`의 UI 별칭 | +| `checkboxes` | 체크박스 렌더링을 사용하는 `multiselect`의 UI 별칭 | + +옵션 형태: + +```ts +options: [ + { value: 'low', label: 'Low' }, + { value: 'high', label: 'High', color: '#e02' }, + { value: 'urgent', label: 'Urgent', color: '#c00' } +] +``` + +## 관계 + +| 타입 | 카디널리티 | 의미 | +|:--|:--|:--| +| `lookup` | 다대일 | 느슨한 참조, 부모를 삭제해도 기본적으로 자식은 삭제되지 않음 | +| `master_detail` | 다대일, 캐스케이딩 | 부모 없이는 자식이 존재할 수 없음, 권한은 부모로부터 상속 | +| `tree` | 자기 참조 | 계층 구조 (동일 객체의 parent_id) | + +공통 옵션: + +```ts +{ + type: 'lookup', + reference: 'account', // target object name + referenceFilters: ['status:active'], // narrow the lookup picker + deleteBehavior: 'set_null' // 'set_null' | 'cascade' | 'restrict' +} +``` + +## 계산형 + +| 타입 | 동작 | 주요 옵션 | +|:--|:--|:--| +| `formula` | 파생 값, 읽기 또는 재계산 시 평가됨 | `expression: F\`record.qty * record.unit_price\`` — [CEL](./cel) 참조 | +| `summary` | 자식 관계에 대한 롤업 | `summaryOperations: { object, field, function }` (`count` \| `sum` \| `avg` \| `min` \| `max`) | +| `autonumber` | 자동 증가 표시 번호 | `format` (예: `TKT-{0000}`), `startAt` | + +수식 예시: + +```ts +{ + name: 'profit_margin', + type: 'formula', + expression: F`(record.revenue - record.cost) / record.revenue * 100` +} +``` + +## 미디어 + +| 타입 | 용도 | 주요 옵션 | +|:--|:--|:--| +| `image` | 이미지 첨부 | `fileAttachmentConfig` (아래 참조) | +| `file` | 모든 파일 | 동일 | +| `avatar` | 프로필 사진 | 정사각형 크롭, 합리적인 기본값 | +| `video` | 비디오 업로드 | 재생 시간 + 썸네일 캡처 | +| `audio` | 오디오 | 파형 미리보기 | + +```ts +fileAttachmentConfig: { + maxSize: 10_000_000, // bytes + allowedTypes: ['image/png','image/jpeg'], + virusScan: true, + storageProvider: 's3', // see Configure → Storage + imageValidation: { minWidth: 200, maxWidth: 4096, generateThumbnails: ['sm','md','lg'] } +} +``` + +## 구조형 + +| 타입 | 저장 내용 | 비고 | +|:--|:--|:--| +| `json` | 임의의 JSON | Postgres에서 JSONB로 저장 | +| `composite` | 명명된 필드를 가진 하위 레코드 | 인라인 구조체, 별도 테이블이 아님 | +| `repeater` | composite 값의 배열 | 자식 객체 없는 일대다 | + +## 향상된 UI + +| 타입 | 비고 | +|:--|:--| +| `location` | 위도/경도 + 정확도 | +| `address` | 도로명 / 시 / 지역 / 우편번호 / 국가 | +| `code` | 소스 코드 필드 — `language`, `theme`, `lineNumbers` | +| `color` | `colorFormat: 'hex' \| 'rgb' \| 'rgba' \| 'hsl'`, `presetColors[]` | +| `rating` | 1–N 별점 — `max`, `icon` | +| `slider` | 슬라이더 UI를 갖춘 범위 제한 숫자 | +| `signature` | 그려진 서명, 이미지로 저장 | +| `qrcode` | 값을 QR로 렌더링 — `qrErrorCorrection: 'L' \| 'M' \| 'Q' \| 'H'` | +| `barcode` | EAN/UPC/Code128 — `barcodeFormat` | +| `progress` | 막대로 렌더링되는 파생 백분율 | +| `tags` | 자동 완성이 있는 자유 형식 태그 배열 | +| `vector` | 임베딩 컬럼 — `vectorConfig: { dimensions, distanceMetric: 'cosine' \| 'euclidean', indexed, indexType: 'hnsw' \| 'ivfflat' }` | + +## 시스템 필드 (모든 객체에 자동 주입됨) + +| 필드 | 타입 | 비고 | +|:--|:--|:--| +| `id` | text (ULID) | 기본 키 | +| `created_at` | datetime | UTC 삽입 시각 | +| `updated_at` | datetime | UTC 마지막 쓰기 시각 | +| `created_by` | lookup → `user` | 삽입한 사람 | +| `updated_by` | lookup → `user` | 마지막으로 쓴 사람 | +| `version` | integer | 낙관적 동시성 토큰 | + +이 필드들은 직접 선언하지 않습니다 — 객체별로 +`ObjectSpec.systemFields: false`로 옵트아웃할 수 있습니다 (대개 좋은 생각은 아닙니다). + +## 필드가 스택을 통해 흐르는 방식 + +``` +*.object.ts (field spec) + │ + ├─► Postgres / MySQL / SQLite column + index + constraint + ├─► REST: validated on POST/PATCH, exposed on GET + ├─► Console: form widget + list column + ├─► AI Builder: tool argument schema (so the AI knows what to ask) + └─► Audit: change-tracked if `auditTrail: true` +``` + +## 함께 보기 + +- [Build → 데이터 모델](/docs/build/data-model) — 필드를 객체로 구성하기 +- [CEL](./cel) — `expression`, `conditionalRequired`, 검증 +- [ObjectQL](./objectql) — 이 필드들을 쿼리하기 +- [REST API](./rest-api) — 이를 생성/소비하는 엔드포인트 +- [`@objectstack/spec/data/field.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/data/field.zod.ts) — 권위 있는 스키마 diff --git a/content/docs/reference/meta.ko.json b/content/docs/reference/meta.ko.json new file mode 100644 index 0000000..876e7ee --- /dev/null +++ b/content/docs/reference/meta.ko.json @@ -0,0 +1,15 @@ +{ + "title": "참조", + "defaultOpen": false, + "pages": [ + "rest-api", + "objectql", + "cel", + "field-types", + "skills-cli", + "cli", + "environment-variables", + "runtime-capabilities", + "security" + ] +} diff --git a/content/docs/reference/objectql.ko.mdx b/content/docs/reference/objectql.ko.mdx new file mode 100644 index 0000000..2df6c0f --- /dev/null +++ b/content/docs/reference/objectql.ko.mdx @@ -0,0 +1,214 @@ +--- +title: ObjectQL +description: /api/v1/data/*, 뷰, 리포트, AI 도구에서 사용하는 구조화된 쿼리 형식입니다. +--- + +# ObjectQL + +ObjectQL은 데이터 엔진이 사용하는 JSON 쿼리 형식입니다. +`/api/v1/data/:object`가 받아들이는 형식이며, 뷰가 컴파일되는 대상이고, +리포트가 직렬화되는 형식이며, AI `query_data` 도구가 생성하는 형식입니다. + +두 가지 형태가 있습니다. + +- **단순 목록(Simple list)** — `where`, `orderBy`, `limit`, `expand` 등을 + 쿼리 문자열 파라미터로 `GET /api/v1/data/:object`에 전달합니다. +- **고급 쿼리(Advanced query)** — 전체 쿼리 본문을 + `/api/v1/data/:object/query`에 POST합니다 (`groupBy`와 + `aggregations`에 필요). + +## 쿼리 형태 + +```ts +{ + object: string, // required — target object name + fields?: string[], // projection (default: all visible fields) + where?: FilterCondition, // see Filters + orderBy?: SortNode[], // [{ field, order: 'asc' | 'desc' }] + limit?: number, // page size + offset?: number, // offset pagination + cursor?: string, // cursor pagination (preferred) + expand?: string[], // batch-resolve relation fields + joins?: JoinNode[], // explicit joins (inner | left | right | full) + groupBy?: (string | GroupByNode)[], + aggregations?: AggregationNode[], // sum/avg/count/min/max/... + having?: FilterCondition, // filter after aggregation + distinct?: boolean +} +``` + +스키마 소스: [`packages/spec/src/data/query.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/data/query.zod.ts). + +## 필터(Filters) + +`where`는 조건들의 트리입니다. 필드 연산자는 +`$`로 접두됩니다. 논리 결합자(`$and`, `$or`, `$not`)는 최상위 레벨에 위치합니다. + +### 비교 + +| 연산자 | 예시 | 비고 | +|:--|:--|:--| +| `$eq` | `{ status: { $eq: "open" } }` | 같음 | +| `$ne` | `{ priority: { $ne: "low" } }` | 같지 않음 | +| `$gt`, `$gte`, `$lt`, `$lte` | `{ amount: { $gte: 1000 } }` | 비교 | + +모든 비교 연산자는 **필드 참조(field reference)**도 받아들여 +필드 간 비교를 할 수 있습니다: `{ end_at: { $gt: { $field: "start_at" } } }`. + +### 집합 및 범위 + +| 연산자 | 예시 | +|:--|:--| +| `$in` | `{ status: { $in: ["new","open"] } }` | +| `$nin` | `{ owner_id: { $nin: ["u_1","u_2"] } }` | +| `$between` | `{ created_at: { $between: ["2026-01-01","2026-02-01"] } }` | + +### 문자열 + +| 연산자 | 예시 | 비고 | +|:--|:--|:--| +| `$contains` | `{ subject: { $contains: "refund" } }` | 대소문자 구분 안 함 | +| `$notContains` | `{ notes: { $notContains: "test" } }` | | +| `$startsWith` | `{ email: { $startsWith: "@" } }` | | +| `$endsWith` | `{ email: { $endsWith: "@acme.com" } }` | | + +### Null 및 존재 여부 + +| 연산자 | 예시 | +|:--|:--| +| `$null` | `{ closed_at: { $null: true } }` | +| `$exists` | `{ external_id: { $exists: false } }` | + +### 논리 결합자 + +```jsonc +{ + "$and": [ + { "status": { "$eq": "open" } }, + { + "$or": [ + { "priority": { "$in": ["high", "urgent"] } }, + { "due_at": { "$lt": { "$cel": "now()" } } } + ] + } + ] +} +``` + +CEL 표현식은 `{ "$cel": "..." }`로 삽입할 수 있습니다 — 서버가 쿼리 시점에 +평가하는 "현재 기준(now-relative)" 필터에 유용합니다. + +## 정렬 + +```jsonc +"orderBy": [ + { "field": "priority", "order": "desc" }, + { "field": "created_at", "order": "asc" } +] +``` + +쿼리 문자열 약식 표기: `?orderBy=-priority,created_at`. + +## 페이지네이션 + +**커서(권장).** 응답에 `nextCursor`가 포함됩니다. 이를 다음 요청의 +`cursor`로 다시 전달하세요. + +```http +GET /api/v1/data/ticket?limit=50&orderBy=-created_at +→ { items: [...], nextCursor: "eyJ..." } + +GET /api/v1/data/ticket?limit=50&cursor=eyJ... +``` + +**오프셋.** 더 단순하지만 대용량 테이블에서는 성능이 저하됩니다. + +```http +GET /api/v1/data/ticket?limit=50&offset=200 +``` + +런타임은 `ObjectSpec.maxPageSize`를 통해 객체별로 `limit`을 제한합니다 +(기본값 200). + +## 관계 — `expand` + +`expand`는 외래 키를 일괄 해석하여 N+1 문제를 방지합니다. + +```jsonc +{ + "object": "support_ticket", + "expand": ["assignee", "customer.account"], + "limit": 20 +} +``` + +각 티켓을 단순 ID가 아니라 `assignee`와 `customer.account`가 +중첩 객체로 구체화된 형태로 반환합니다. + +## 조인(Joins) + +메타데이터 그래프 외부에서 임시 조인을 수행하려면 다음과 같이 합니다. + +```jsonc +"joins": [ + { "type": "left", "object": "user", "as": "u", "on": "assignee_id = u.id" } +] +``` + +`type`: `inner` | `left` | `right` | `full`. 조인된 테이블은 `as` +별칭을 통해 `where`, `orderBy`, `aggregations`에서 접근할 수 있습니다. + +## 집계(Aggregation) + +`POST /api/v1/data/:object/query`에서만 가능합니다. + +```jsonc +{ + "object": "order", + "where": { "status": { "$ne": "cancelled" } }, + "groupBy": [ + "customer_id", + { "field": "created_at", "dateGranularity": "month" } + ], + "aggregations": [ + { "function": "sum", "field": "amount", "alias": "total_sales" }, + { "function": "count", "alias": "order_count" } + ], + "having": { "total_sales": { "$gt": 10000 } }, + "orderBy": [{ "field": "total_sales", "order": "desc" }], + "limit": 25 +} +``` + +**함수:** `count`, `sum`, `avg`, `min`, `max`, `count_distinct`, +`array_agg`, `string_agg`. + +**날짜 단위**(시간 버킷 기준 group-by용): +`day` | `week` | `month` | `quarter` | `year`. + +## Distinct + +```jsonc +{ "object": "ticket", "fields": ["status"], "distinct": true } +``` + +## 검색 + +`searchable: true` 필드 전반에 대한 전체 텍스트 검색은 +`GET /api/v1/search?q=...&object=ticket`에서 제공됩니다. 객체별 점수 산정 규칙은 +객체 스펙에서 구성합니다. + +## ObjectQL이 사용되는 곳 + +- `GET /api/v1/data/:object` — 쿼리 문자열 형태 +- `POST /api/v1/data/:object/query` — 전체 본문, 집계 지원 +- 뷰 정의(`filter`, `sort`) — ObjectQL로 컴파일됨 +- 리포트 — ObjectQL로 직렬화됨 +- AI `query_data` 도구 — 승인 큐를 위한 ObjectQL 본문을 생성함 + +## 함께 보기 + +- [REST API](./rest-api) — ObjectQL을 사용하는 엔드포인트 +- [필드 타입](./field-types) — 쿼리 가능한 항목 +- [CEL](./cel) — `$cel` 필터 내부에서 사용하는 표현식 언어 +- [`@objectstack/spec/data/query.zod.ts`](https://github.com/objectstack-ai/framework/blob/main/packages/spec/src/data/query.zod.ts) — 공식 스키마 diff --git a/content/docs/reference/rest-api.ko.mdx b/content/docs/reference/rest-api.ko.mdx new file mode 100644 index 0000000..8ba449a --- /dev/null +++ b/content/docs/reference/rest-api.ko.mdx @@ -0,0 +1,189 @@ +--- +title: REST API +description: ObjectOS가 노출하는 HTTP 표면 — 메타데이터에서 생성되고, 권한으로 범위가 지정되며, OpenAPI로 기술됩니다. +--- + +# REST API + +선언하는 모든 객체는 자동으로 완전한 REST 엔드포인트 세트를 갖게 됩니다. +모든 액션은 `POST`가 되고, 모든 플로우는 +`/flows/...`로의 `POST`가 됩니다. 별도로 작성하거나 배포할 API 계층이 없습니다. + +기본 URL: `https:///api/v1`. +OpenAPI 사양은 [`/api/v1/openapi.json`](#discovery)에서 실시간으로 제공됩니다. + +## Authentication + +ObjectOS는 [Better Auth](https://www.better-auth.com/)를 사용합니다. `/api/v1` +아래의 모든 라우트는 명시적으로 **public**으로 표시되지 않는 한 인증된 세션이 +필요합니다([Public forms](#public-forms) 참고). 인증 방식은 +`apps//auth.config.ts`에서 구성되며, 일반적으로 세션 쿠키 또는 +`/api/v1/auth/sign-in`에서 받은 bearer 토큰입니다. + +권한은 **라우트별, 레코드별**로 확인됩니다. 아래에서 권한 키(예: `ai:chat`)가 +표시된 라우트는 `requirePermission(...)`을 호출하며, 레코드 수준 접근은 데이터 +엔진 내부의 RBAC + 행 수준 보안 + 필드 수준 보안에 의해 적용됩니다. + +## Discovery + +| Path | Method | Purpose | +|:--|:--|:--| +| `/api/v1` | GET | 서비스 디스커버리 — 등록된 모든 라우트, 버전, 범위 지정 모드를 나열 | +| `/api/v1/openapi.json` | GET | 실행 중인 런타임의 모든 엔드포인트에 대한 OpenAPI 3.0 사양 | +| `/api/v1/search` | GET | 모든 객체의 검색 가능 필드에 대한 전문(full-text) 검색 | + +## Data — `/api/v1/data/*` + +선언하는 모든 객체에 대한 CRUD + 고급 쿼리. `:object`는 객체의 +`name`입니다(snake_case). + +| Path | Method | Purpose | +|:--|:--|:--| +| `/data/:object` | GET | 목록 / 쿼리 — `where`, `orderBy`, `limit`, `offset`, `cursor`, `expand`, `select`를 쿼리 파라미터로 전달 | +| `/data/:object/query` | POST | 고급 쿼리 — `groupBy` + `aggregations`를 포함한 완전한 ObjectQL 본문([ObjectQL](./objectql) 참고) | +| `/data/:object/:id` | GET | 레코드 하나 조회. `?select=` 및 `?expand=` 지원 | +| `/data/:object` | POST | 생성. 새 레코드와 함께 `201` 반환 | +| `/data/:object/:id` | PATCH | 업데이트. 낙관적 동시성 제어를 위해 `If-Match: ` 헤더(또는 본문의 `expectedVersion`)를 전달 — 충돌 시 `409 CONCURRENT_UPDATE` | +| `/data/:object/:id` | DELETE | 삭제. 동일한 `If-Match` 규칙 적용 | +| `/data/:object/import` | POST | 대량 가져오기(CSV 또는 JSON). 본문: `{ format, csv? \| rows?, mapping?, dryRun? }`. 요청당 최대 5,000행 | +| `/data/:object/export` | POST | 레코드를 `csv` / `xlsx` / `pdf` / `json`으로 내보내기 | +| `/data/:object/:id/shares` | GET / POST | 레코드별 공유 — 권한 부여 목록 조회, 접근 권한 부여 | +| `/data/:object/:id/shares/:shareId` | DELETE | 공유 취소 | +| `/data/lead/:id/convert` | POST | Salesforce 스타일의 리드 전환(CRM 템플릿). 본문: `{ accountId?, contactId?, createOpportunity? }` | + +### Quick example + +```http +GET /api/v1/data/support_ticket?where={"status":{"$eq":"open"}}&orderBy=-created_at&limit=20 +Authorization: Bearer +``` + +```json +{ + "items": [ { "id": "...", "subject": "...", "status": "open" } ], + "total": 137, + "nextCursor": "eyJpZCI6IjAxSDA..." +} +``` + +## AI — `/api/v1/ai/*` + +[AI Builder](/docs/build/ai-builder)와 [agents](/docs/build/agents)가 사용하는 +엔드포인트입니다. + +| Path | Method | Permission | Purpose | +|:--|:--|:--|:--| +| `/ai/models` | GET | `ai:chat` | 구성된 제공자에서 사용 가능한 모델 나열 | +| `/ai/chat` | POST | `ai:chat` | 단발성 채팅 완성(동기) | +| `/ai/chat/stream` | POST | `ai:chat` | 스트리밍 채팅(SSE) | +| `/ai/complete` | POST | `ai:chat` | 원시(raw) 완성 엔드포인트 | +| `/ai/conversations` | GET / POST | `ai:chat` | 영속 대화 목록 조회 / 생성 | +| `/ai/conversations/:id` | GET / DELETE | `ai:chat` | 대화 읽기 / 삭제 | +| `/ai/conversations/:id/messages` | POST | `ai:chat` | 사용자 메시지를 추가하고 에이전트 실행 | +| `/ai/pending-actions` | GET | `ai:read` | HITL 승인 큐 — AI가 제안한 변경 사항 | +| `/ai/pending-actions/:id` | GET | `ai:read` | 큐에 대기 중인 변경 사항 검사 | +| `/ai/pending-actions/:id/approve` | POST | `ai:approve` | 변경 사항 적용 | +| `/ai/pending-actions/:id/reject` | POST | `ai:approve` | 변경 사항 폐기 | + +`ai:read`와 `ai:approve`의 분리는 AI Builder를 최종 사용자에게 안전하게 +만드는 보안 기본 요소입니다. + +## Actions — `/api/v1/actions/*` + +선언하는 모든 `*.action.ts`는 엔드포인트가 됩니다. + +| Path | Method | Purpose | +|:--|:--|:--| +| `/actions/:action` | POST | 액션 호출. 본문은 액션의 입력 스키마. 권한과 입력 검증은 액션 선언에서 가져옴 | + +각 액션은 AI에게도 `action_` 도구로 노출됩니다 — +[Actions](/docs/build/actions) 참고. + +## Flows — `/api/v1/flows/*` + +| Path | Method | Purpose | +|:--|:--|:--| +| `/flows/:flow/start` | POST | 플로우 시작. 본문은 플로우 입력 | +| `/flows/:flow/runs/:runId` | GET | 실행 중 / 완료된 플로우의 상태 + 단계 출력 | +| `/flows/:flow/runs/:runId/cancel` | POST | 진행 중인 실행 취소 | + +## Metadata — `/api/v1/meta/*` + +실행 중인 런타임의 메타데이터에 대한 읽기 전용 인트로스펙션. 도구와 +Console에 유용합니다. + +| Path | Method | Purpose | +|:--|:--|:--| +| `/meta` | GET | 모든 메타데이터 유형(`object`, `view`, `action`, `flow`, `agent`, …) | +| `/meta/:type` | GET | 한 유형의 항목 나열. `?package=`로 필터링 | +| `/meta/:type/:name` | GET | 항목 하나 조회. `?layers=true`는 3-state 디프 뷰(base / package / overlay) 반환 | +| `/meta/:type/:name/references` | GET | 이 항목을 참조하는 모든 메타데이터 항목 찾기 | +| `/meta/:type/:name/history` | GET | 해당 항목의 감사 추적 | +| `/meta/:type/:section/:name` | GET / POST | 섹션화된 메타데이터 항목 읽기 / upsert | +| `/meta/:type/:section/:name` | DELETE | 섹션화된 항목 삭제 | + +## Sharing & approvals + +| Path | Method | Purpose | +|:--|:--|:--| +| `/sharing/rules` | GET / POST / DELETE | 공유 규칙 관리 | +| `/sharing/rules/:idOrName/evaluate` | POST | 컨텍스트에 대해 규칙을 모의 실행(dry-run) | +| `/approvals/processes` | GET / POST | 승인 프로세스 정의 | +| `/approvals/requests` | GET / POST | 승인 요청 제출 / 나열 | +| `/approvals/requests/:id/approve` | POST | 결정: 승인 | +| `/approvals/requests/:id/reject` | POST | 결정: 거부 | + +## Reports & search + +| Path | Method | Purpose | +|:--|:--|:--| +| `/reports` | GET / POST / PATCH | 리포트 CRUD | +| `/reports/:id/run` | POST | 리포트 실행 — 집계된 행 반환 | +| `/reports/:id/schedule` | POST | 반복 전달 예약 | + +## Public forms + +**인증되지 않은** 유일한 라우트로, `FormView.sharing.allowAnonymous`를 통해 +옵트인합니다. + +| Path | Method | Auth | +|:--|:--|:--| +| `/forms/:slug` | GET | public — 폼 스키마 반환 | +| `/forms/:slug/submit` | POST | public — 응답 제출 | + +## Utilities + +| Path | Method | Permission | Purpose | +|:--|:--|:--|:--| +| `/email/send` | POST | `email:send` | 구성된 제공자를 통해 이메일 발송 | + +## Error envelope + +2xx가 아닌 모든 응답은 동일한 형태를 따릅니다: + +```json +{ + "error": { + "code": "PERMISSION_DENIED", + "message": "Missing ai:approve on conversation 01H…", + "details": { "permission": "ai:approve", "subject": "01H…" } + } +} +``` + +일반적인 코드: `UNAUTHENTICATED`, `PERMISSION_DENIED`, +`VALIDATION_ERROR`, `NOT_FOUND`, `CONCURRENT_UPDATE`, +`RATE_LIMITED`, `INTERNAL`. + +## Versioning + +`/api/v1`은 **안정(stable)** API입니다. 호환성을 깨는 변경 사항은 `/api/v2`로 +가며, 두 버전은 하나의 마이너 릴리스 동안 병렬로 제공됩니다. 현재 버전과 +지원 중단(deprecation) 기한은 `GET /api/v1`에 있습니다. + +## See also + +- [ObjectQL](./objectql) — `/data/*`가 사용하는 쿼리 언어 +- [Field types](./field-types) — 데이터가 가질 수 있는 형태 +- [Build → Actions](/docs/build/actions) — 커스텀 엔드포인트 선언 +- [Security](./security) — 모든 라우트가 실행 전에 확인하는 것 diff --git a/content/docs/reference/runtime-capabilities.ko.mdx b/content/docs/reference/runtime-capabilities.ko.mdx new file mode 100644 index 0000000..9e0869d --- /dev/null +++ b/content/docs/reference/runtime-capabilities.ko.mdx @@ -0,0 +1,75 @@ +--- +title: 런타임 기능 +description: ObjectOS가 ObjectStack 프레임워크 패키지에서 로드할 수 있는 기능. +--- + +# 런타임 기능 + +ObjectOS는 모든 프로젝트에 대해 기본 런타임을 로드한 다음, 애플리케이션 +아티팩트가 선언한 선택적 기능을 설치합니다. + +## 기본 런타임 + +기본 프로젝트 커널에는 다음이 포함됩니다. + +- ObjectKernel 라이프사이클 및 서비스 레지스트리; +- ObjectQL 데이터 엔진; +- 구성된 데이터 드라이버; +- 메타데이터 서비스; +- 애플리케이션 아티팩트 등록; +- `OS_AUTH_SECRET`이 구성된 경우 인증; +- RBAC, 행 수준 보안, 필드 보안을 위한 보안 플러그인; +- i18n 서비스. + +## 선택적 기능 + +아티팩트는 `requires` 목록에 기능을 선언할 수 있습니다. ObjectOS는 +이미지에 존재하는 경우 일치하는 프레임워크 패키지를 로드합니다. + +| 기능 | 패키지 | 용도 | +|---|---|---| +| `automation` | `@objectstack/service-automation` | Flow/DAG 실행 및 자동화 노드 | +| `ai` | `@objectstack/service-ai` | LLM 어댑터, 대화, 도구, SSE 라우트 | +| `analytics` | `@objectstack/service-analytics` | 큐브, 분석 쿼리, 리포팅 데이터 | +| `audit` | `@objectstack/plugin-audit` | 감사 로그 객체 및 감사 추적 | +| `cache` | `@objectstack/service-cache` | 캐시 추상화 및 어댑터 | +| `storage` | `@objectstack/service-storage` | 파일/객체 스토리지 서비스 | +| `queue` | `@objectstack/service-queue` | 큐 추상화 및 워커 | +| `job` | `@objectstack/service-job` | 예약된/백그라운드 작업 | +| `realtime` | `@objectstack/service-realtime` | WebSocket 및 pub/sub 실시간 | +| `feed` | `@objectstack/service-feed` | 댓글, 반응, 구독, 활동 피드 | +| `settings` | `@objectstack/service-settings` | 설정 매니페스트 및 K/V 리졸버 | + +기능이 요청되었지만 이미지에 해당 패키지가 포함되어 있지 않은 경우, +**ObjectOS는 경고를 기록하고 해당 기능을 비활성화한 상태로 계속 +실행됩니다.** 이는 의도된 동작으로, 개발 중에 선택적 패키지가 누락된 +경우에도 런타임이 부팅 가능하도록 유지하기 위함입니다. 하지만 +**프로덕션에서는 실질적인 위험입니다**: + +- `@objectstack/plugin-audit` 없이 이미지에 로드된 감사 필수 앱은 + 정상적으로 시작되지만 **감사 로그를 기록하지 않습니다**. 어떤 오류도 + 없이 컴플라이언스 증거가 누락됩니다. +- `@objectstack/service-job` 없이 이미지에 로드된 작업 기반 앱은 + 정상적으로 시작되지만 **예약된 작업을 조용히 실행하지 않습니다**. + +**프로덕션 체크리스트:** + +1. 기능 경고를 배포 실패로 취급하십시오. 시작 로그에서 + `capability not loaded`(또는 이에 상응하는 메시지)를 검색하고, 해당 + 메시지가 나타나면 준비 상태 프로브 / 롤아웃을 실패시키십시오. +2. 아티팩트가 `requires`에 선언한 모든 패키지를 포함하는 런타임 + 이미지에 고정하십시오. +3. 아티팩트를 게시할 때, 운영자가 일치하는 이미지를 확인할 수 있도록 + 해당 `requires` 목록을 함께 문서화하십시오. + +## API 표면 + +ObjectOS는 일반적으로 다음을 노출합니다. + +- 생성된 REST API; +- `/api/v1/auth/*` 아래의 인증 엔드포인트; +- 메타데이터 및 i18n 엔드포인트; +- 활성화된 기능에 대한 서비스 엔드포인트. + +GraphQL과 OData는 프레임워크 수준의 기능이며, 배포된 런타임 패키지에 +포함되어 활성화된 경우에만 지원되는 것으로 문서화해야 합니다. diff --git a/content/docs/reference/security.ko.mdx b/content/docs/reference/security.ko.mdx new file mode 100644 index 0000000..bb29102 --- /dev/null +++ b/content/docs/reference/security.ko.mdx @@ -0,0 +1,271 @@ +--- +title: 보안 및 규정 준수 +description: 무엇이, 어떻게, 누구의 책임 하에 보호되는지 — 보안 검토를 위한 안내입니다. +--- + +# 보안 및 규정 준수 + +이 페이지는 보안 검토 담당자, IT 관리자, 그리고 "이것을 도입해도 안전한가?"에 +답해야 하는 모든 사람을 위한 것입니다. + +## 한 문장으로 보는 위협 모델 + +ObjectOS는 **당신의** 네트워크 내부에서 단일 Node.js 프로세스로 실행되며, +**당신의** 데이터베이스와 통신하고, 절대 외부로 연락(call home)하지 않습니다. +침해가 발생했을 때의 피해 범위(blast radius)는 연결된 데이터베이스의 +데이터에 한정됩니다 — 그 이상은 없습니다. + +## 데이터 레지던시 + +| 데이터 분류 | 저장 위치 | 네트워크를 벗어나는가? | +|---|---|---| +| 비즈니스 레코드 | 당신의 데이터베이스 | **아니요** | +| 사용자 계정, 세션, OAuth 토큰 | 당신의 데이터베이스 | **아니요** | +| 감사 로그 | 당신의 데이터베이스 | **아니요** | +| 설정, API 키 | 당신의 데이터베이스 / 당신의 시크릿 매니저 | **아니요** | +| 업로드된 파일 | 당신의 디스크 또는 S3 호환 버킷 | **아니요** | +| 텔레메트리 / 사용 데이터 | — | **수집하지 않음** | + +ObjectOS는 명시적으로 구성하지 않는 한 **외부로의 호출을 전혀 하지 +않습니다**(OIDC 디스커버리, 이메일 공급자, AI 공급자, 웹훅 대상, +외부 스토리지). 외부로 연락하지 않고, 라이선스 서버를 확인하지 않으며, +업데이트를 위해 핑을 보내지도 않습니다. + +## 암호화 + +| 계층 | 메커니즘 | 책임 주체 | +|---|---|---| +| 전송 중 (브라우저 ↔ ObjectOS) | TLS, 당신의 엣지 / 인그레스에서 종료 | 당신 | +| 전송 중 (ObjectOS ↔ 데이터베이스) | 드라이버 수준 TLS (Postgres `sslmode=require`, MongoDB `tls=true`, …) | 당신 — 연결 문자열을 설정 | +| 저장 시 (비즈니스 데이터) | 데이터베이스 네이티브 (예: Postgres TDE, RDS 암호화) | 당신 | +| 저장 시 (업로드된 파일) | 스토리지 네이티브 (S3 SSE, R2 기본값, 디스크 수준 FDE) | 당신 | +| DB 내 시크릿 (설정, OIDC 클라이언트 시크릿) | 설정 서비스에 의해 암호화됨 | ObjectOS | +| 세션 쿠키 / 토큰 | `OS_AUTH_SECRET`로 HMAC 서명됨 | ObjectOS | +| API 키 값 | DB에 **해시 처리됨** — 유출된 DB 행으로는 키를 복원할 수 없음 | ObjectOS | + +## 인증 + +기본 제공 (`@objectstack/plugin-auth`를 통해, Better Auth 기반): + +- 검증 + 재설정이 포함된 이메일/비밀번호 +- 취소(revocation) 기능이 있는 세션 관리 +- 소셜 OAuth (Google, GitHub, Microsoft, Apple, …) +- 엔터프라이즈 OIDC/SSO (Okta, Entra ID, Keycloak, Ping) +- 2단계 인증 (TOTP) +- 패스키 / WebAuthn +- 매직 링크 +- CLI/브라우저 디바이스 플로우 +- API 키 (해시 처리됨, 만료 가능, 취소 가능, 사용자에 바인딩됨) + +[Authentication](/docs/configure/authentication)을 참조하세요. + +## 권한 부여 + +계층화된 적용 (`@objectstack/plugin-security`를 통해): + +1. **오브젝트 권한** — 권한 집합별, 오브젝트별 CRUD +2. **행 수준 보안** — 쿼리에 주입되는 선언적 정책 표현식이며, + 선택 사항이 아닙니다 +3. **필드 수준 보안** — 응답에서 제거되거나 쓰기 시 거부되는 필드 +4. **조직 범위 지정** — 멀티 테넌트 격리; 우회하려면 명시적인 + `viewAllRecords`가 필요함 + +시스템 컨텍스트 작업은 내부 작업 / 마이그레이션이 실행될 수 있도록 +검사를 우회합니다 — 이러한 경로는 감사 가능합니다. + +[Permissions](/docs/configure/permissions)을 참조하세요. + +## 감사 및 증거 + +감사 기능이 로드되면 (`@objectstack/plugin-audit`): + +- 모든 오브젝트에 걸친 모든 CRUD 작업 → 감사 행. +- 필드 변경에 대한 변경 전/후 값. +- 인증, 권한 부여, 세션 취소 이벤트. +- 감사 행은 **불변(immutable)**입니다: 수정할 수 없으며 아카이브만 가능합니다. +- 보존 기간은 구성 가능하며, DB의 아카이브 정책과 함께 사용하세요. + +이것은 SOC 2 CC6/CC7, ISO 27001 A.12.4, HIPAA §164.312(b), GDPR 제30조에 +대한 증거 기반이 됩니다. + +## 규정 준수 프레임워크 + +ObjectOS는 모든 일반적인 프레임워크가 요구하는 **기술적 기본 요소(primitives)**를 +제공합니다. 인증(certification)은 소프트웨어가 아닌 배포의 속성이지만, +컨트롤은 깔끔하게 매핑됩니다: + +| 프레임워크 | ObjectOS가 제공하는 것 | +|---|---| +| **SOC 2** | 접근 제어 (CC6), 변경 관리 (감사 로그), 암호화 (배포), 모니터링 (옵저버빌리티), 백업 (운영/백업) | +| **ISO 27001** | A.5 정책 (RBAC), A.8 자산 관리 (오브젝트 카탈로그), A.9 접근 제어, A.12 운영, A.18 준수 | +| **HIPAA** | 접근 제어 (§164.312(a)), 감사 제어 (§164.312(b)), 무결성 (불변 감사), 전송 보안 (TLS) | +| **GDPR** | 제30조 처리 기록 (감사), 제32조 처리의 보안, 제17조 삭제권 (소프트 + 하드 삭제 지원), 데이터 레지던시 (리전을 선택) | +| **CCPA / 중국 DSL / 러시아 152-FZ** | 올바른 리전에서의 셀프 호스팅이 레지던시 요건을 충족하며, 접근 제어 + 감사가 대부분의 보고 의무를 커버합니다 | + +ObjectOS 자체는 **인증되지 않았습니다**. 인증은 바이너리가 아닌 실행 중인 +배포에 대한 것이기 때문입니다. 당신의 배포는 인증받을 수 있으며 — +이미 인증받은 곳도 많습니다. + +## 시크릿 처리 + +| 시크릿 | 보관 위치 | +|---|---| +| `OS_AUTH_SECRET` | 당신의 시크릿 매니저 (Vault, AWS Secrets Manager, k8s Secret); 환경 변수로 주입 | +| 자격 증명이 포함된 데이터베이스 URL | 위와 동일 | +| OIDC 클라이언트 시크릿 | 위와 동일 | +| OAuth 공급자 시크릿 | 위와 동일 | +| API 공급자 키 (이메일, 스토리지, AI) | 위와 동일 | +| DB에 저장된 설정 | 설정 서비스에 의해 저장 시 암호화됨 | + +시크릿을 아티팩트(`objectstack.json`), Docker 이미지, compose 파일, 또는 +Git에 절대 **포함하지 마세요**. Console의 설정 UI는 환경 변수로 관리되는 +값을 잠금 상태로 표시하므로, 운영자가 실수로 이를 재정의할 수 없습니다. + +## 네트워크 모델 + +필수 인바운드: +- 당신의 인그레스 / 로드 밸런서에서 ObjectOS의 `:3000`(기본값)으로의 HTTPS. + +필수 아웃바운드 (다음 기능을 구성하는 경우에만): +- 당신의 데이터베이스 (Postgres / Mongo / Turso / …). +- S3 호환 스토리지 (`storage` 기능이 S3 어댑터와 함께 활성화된 경우). +- OIDC 디스커버리 URL (SSO가 활성화된 경우). +- 이메일 공급자 API (Resend / Postmark). +- AI 공급자 API (OpenAI / Anthropic / Google / …). +- 웹훅 대상. + +이것이 전체 이그레스(egress) 표면입니다. 이보다 더 많은 것을 차단하는 +배포에 대해서는 [Air-gapped](/docs/deploy/air-gapped)를 참조하세요. + +## AI: 도구, 승인, 격리 + +AI Builder는 당신이 노출하게 될 가장 보안에 민감한 표면이므로, 위의 +모든 것에 더해 자체적인 적용 계층을 가지고 있습니다. + +### AI가 상태를 변경하는 방법 + +모델은 당신의 데이터베이스에 **직접 쓸 수 없습니다**. 상태가 변경되는 +유일한 방법은 구조화된 도구 호출을 발행하는 것이며, AI 서비스가 이를 +수신하고, 검증하고, 큐에 넣습니다. 그 연쇄 과정은 다음과 같습니다: + +```text +user prompt + → model emits tool call (e.g. add_field { object: 'ticket', name: 'severity', type: 'select' }) + → AI service validates payload against the tool's Zod schema + → if the tool is "mutating": queue as pending action (no state change yet) + → human reviewer approves → mutation applied → audit row written + → if the tool is "read-only": run immediately, response returned to model +``` + +11개의 기본(first-party) 메타데이터 도구 +([Build → AI Builder 참조](/docs/build/ai-builder))와 더불어, 선언된 +액션당 하나의 `action_` 도구가 있습니다. 모든 도구 — 기본이든 +커스텀이든 — 는 동일한 생명 주기를 갖습니다. + +### 권한 키 + +| 키 | 부여 내용 | +|:--|:--| +| `ai:chat` | 대화 진행; 모델 소비; 에이전트가 읽기 전용 도구를 호출하도록 허용 | +| `ai:complete` | 원시 컴플리션 엔드포인트 (에이전트 루프 없음) | +| `ai:conversations` | 대화 목록 조회 / 검사 / 삭제 (RBAC 범위에 따라 본인 또는 전체) | +| `ai:agents` | 에이전트 메타데이터 관리 (호출하려면 `ai:chat`과 함께) | +| `ai:tools` | 도구 카탈로그 목록 조회 | +| `ai:execute` | REST를 통해 도구를 직접 호출 (고급 — 보통 앰비언트 에이전트만 필요) | +| `ai:read` | 대기 중 액션 큐 및 모델 목록 읽기 | +| `ai:approve` | 큐에 들어간 변경 승인 / 거부 | +| `ai:admin` | 전체 AI 서비스 관리 | + +핵심적인 구분은 **`ai:chat` ≠ `ai:approve`**입니다. 어시스턴트가 작동하도록 +대부분의 사용자에게 `ai:chat`을 부여하고, 구조적 변경을 검토해야 하는 +관리자 / 앱 소유자에게는 `ai:approve`를 따로 두세요. 따라서 최종 사용자는 +안전하게 "바이브 빌드(vibe-build)"할 수 있습니다 — 그들이 할 수 있는 최악의 +일은 다른 누군가가 수락해야 하는 잘못된 변경을 큐에 넣는 것뿐입니다. + +### 테넌트 격리 + +- 에이전트, 대화, 지식 베이스, 대기 중 액션은 하나의 **Environment**(테넌트)로 + 범위가 지정됩니다. 테넌트 A는 테넌트 B의 대화, AI가 제안한 도구, 또는 + 지식 코퍼스를 볼 수 없습니다 — 동일한 marketplace 패키지에서 동일한 + 에이전트 정의가 설치되었더라도 마찬가지입니다. +- 메타데이터 도구(`create_object`, `add_field`, …)는 호출자의 테넌트 내 + **활성 패키지**에 대해 작동합니다. 그 외부에는 도달할 수 없습니다. +- 도구 입력은 CEL로 검증되며, 엔진은 예약된 시스템 패키지(`sys.*`) 또는 + 다른 테넌트의 오브젝트 이름에 대한 참조를 거부합니다. + +### 감사 이벤트 + +감사 기능이 로드되면: + +- `ai.chat.message` — 모든 사용자 / 어시스턴트 메시지, 모델 + 토큰 수 포함 +- `ai.tool.call` — 도구 이름, 검증된 입력, 전체 출력 (또는 오류) +- `ai.pending_action.queued` — 제안된 변경, 전체 diff +- `ai.pending_action.approved` / `.rejected` — 누가, 언제, 왜 결정했는지 +- `ai.metadata.applied` — 메타데이터 스토어에 대한 실제 쓰기, diff 포함 + +이 행들은 불변이며 보안 검토나 차지백(chargeback)을 위해 내보낼 수 +있습니다. (공급자, 모델, 사용자)별 토큰 수는 비용 귀속(cost attribution)에 +활용됩니다. + +### 프롬프트 인젝션 대응 태세 + +간접 프롬프트 인젝션(예: 에이전트가 검색하는 문서 내의 악성 콘텐츠)은 +실제 위험입니다. ObjectOS는 구조적으로 피해 범위를 줄입니다: + +- AI는 **도구 검증을 우회할 수 없습니다** — 악성 페이로드를 발행하도록 + 설득당하더라도, Zod 스키마가 엔진에 도달하기 전에 잘못된 입력을 + 거부합니다. +- 변경(mutating) 도구는 항상 큐에 들어갑니다. 주입된 프롬프트는 + 조용히 데이터베이스에 쓸 수 없습니다. +- 도구 호출은 모델의 서비스 계정 권한이 아닌 **최종 사용자의 권한**을 + 상속합니다. 사용자는 Console이나 REST를 통해 스스로 할 수 없는 일을 + AI를 사용해 결코 할 수 없습니다. +- 에이전트에 로드된 스킬은 버전 관리되고 명시적입니다 — + [Build → IDE Skills](/docs/build/ai-skills)와 + [Build → AI Builder](/docs/build/ai-builder)를 참조하세요. + +### 외부 AI 공급자 데이터 흐름 + +공급자(OpenAI, Anthropic, …)를 구성하면, 다음 항목만 당신의 네트워크를 +벗어납니다: + +- 모델이 필요로 하는 대화 기록 (당신의 AI 서비스 `redact` 구성에 따름 — + [Configure → AI](/docs/configure/ai) 참조) +- 도구 정의 (이름, JSON 스키마 — 레코드 데이터 없음) +- 모델이 계속 진행하는 데 필요한 도구 출력 (예: 사용자가 명시적으로 + 요청한 쿼리 결과) + +에어갭 배포의 경우, AI 서비스를 로컬 Ollama / vLLM / TGI 엔드포인트로 +지정하면 동일한 흐름이 당신의 경계 내부에 머무릅니다. + +## 취약점 공개 + +보안 문제는 **security@objectstack.ai**로 비공개로 신고하세요. 영업일 기준 +1일 이내에 응답합니다. 보안 문제에 대해 공개 GitHub 이슈를 작성하지 마세요. + +## 공급망 + +- [github.com/objectstack-ai/objectos](https://github.com/objectstack-ai/objectos)에서 + 재현 가능한 빌드 출처(provenance)와 함께 게시된 사전 빌드 이미지. +- 모든 `@objectstack/*` 패키지는 GitHub에 소스가 공개되어 있습니다 — + Apache-2.0, 난독화 없음. +- 드리프트를 방지하기 위해 프로덕션에서는 SHA로 고정된 이미지 태그 + (`sha-`)를 사용하세요. [Docker](/docs/deploy/docker)를 참조하세요. + +## 권장 하드닝 체크리스트 + +- [ ] 실제 인증서로 엣지에서 TLS 종료. +- [ ] `OS_AUTH_SECRET`은 32바이트 이상의 랜덤 값이며, 시크릿 매니저에 보관됨. +- [ ] 데이터베이스 연결이 TLS를 사용함. +- [ ] TLS 검증 후 HSTS 활성화됨. +- [ ] CORS 오리진이 명시적임 (자격 증명과 함께 `*`를 절대 사용하지 않음). +- [ ] 인증 엔드포인트에 속도 제한 적용 (`10/min/IP` 권장). +- [ ] 감사 보존 기간이 정책과 일치함. +- [ ] 사람 계정에는 OIDC; 머신 계정에는 API 키. +- [ ] 백업 + 복원 훈련을 실행하고 시간을 측정함. +- [ ] 네거티브 테스트: 조직 간 접근 거부, 필드 보안 유지, 만료된 세션 거부. +- [ ] 이미지가 `sha-` 또는 semver 태그로 고정됨. +- [ ] 각 릴리스 전 CI에서 `os doctor`가 깨끗함. + +전체 출시 체크리스트는 [Production Readiness](/docs/operate/production)를 +참조하세요. diff --git a/content/docs/reference/skills-cli.ko.mdx b/content/docs/reference/skills-cli.ko.mdx new file mode 100644 index 0000000..43e4cfd --- /dev/null +++ b/content/docs/reference/skills-cli.ko.mdx @@ -0,0 +1,98 @@ +--- +title: skills CLI +description: ObjectOS 스킬 번들을 코딩 에이전트에 설치하는 npx 명령입니다. +--- + +# `skills` CLI + +[`skills`](https://www.npmjs.com/package/skills) CLI([vercel-labs/skills](https://github.com/vercel-labs/skills) +제작)는 도메인 범위의 지침을 AI 코딩 에이전트에 배포하는 표준 +방식입니다. ObjectOS는 이를 통해 9개의 공식 스킬을 게시합니다. + +이 페이지는 참조 카드입니다. 개념적 개요는 +[Build → IDE Skills](/docs/build/ai-skills)를 참고하세요. + +## 설치 + +```bash +npx skills add objectstack-ai/framework +``` + +처음 실행하면 `npx`를 통해 npm에서 [`skills`](https://www.npmjs.com/package/skills) +패키지를 다운로드한 다음 +[github.com/objectstack-ai/framework](https://github.com/objectstack-ai/framework) +(`skills//` 폴더)에서 스킬 번들을 가져와 프로젝트의 에이전트 +설정에 기록합니다. + +최신 버전을 가져오려면 언제든지 다시 실행하세요. + +## 무엇이 기록되나요 + +프로젝트에 있는 내용을 기반으로 자동으로 감지됩니다. + +| 에이전트 | 기록되는 경로 | +|:--|:--| +| Claude Code | `.claude/skills//SKILL.md` | +| Cursor | `.cursor/rules/.mdc` | +| GitHub Copilot | `.github/copilot-instructions.md` (통합) | +| Codex / Codex CLI | `AGENTS.md` (통합) | +| Gemini CLI | `.gemini/skills//` | +| Windsurf, Cline, Continue, Roo, Goose, Kiro, opencode, … | 각자의 네이티브 스킬 / 규칙 형식 | + +CLI가 기록할 내용을 확인하려면 `npx skills detect`를 실행하세요. + +## ObjectOS가 게시하는 항목 + +| 스킬 | 로드 대상 | +|:--|:--| +| `objectstack-platform` | `defineStack`, 드라이버, 어댑터, 플러그인, 서비스, `os` CLI, 배포 | +| `objectstack-data` | `*.object.ts`, `*.seed.ts`, 필드, 관계, 검증, 인덱스, RLS, 라이프사이클 훅 | +| `objectstack-query` | [ObjectQL](./objectql) — 필터, 정렬, 페이지네이션, 집계, 조인, 전문 검색 | +| `objectstack-ui` | [Views](/docs/build/views), Apps, Pages, Dashboards, Reports, Charts, Actions | +| `objectstack-automation` | Flows, Workflows, Triggers, Approvals, 스케줄, 웹훅 | +| `objectstack-ai` | Agents, Tools, Skills, Conversations, Model Registry, MCP | +| `objectstack-api` | REST/GraphQL 엔드포인트, 인증, 실시간, 오류 엔벨로프 | +| `objectstack-i18n` | 번역 번들, 로케일 폴백 | +| `objectstack-formula` | [CEL](./cel) 표현식 — 수식 필드, 술어, 조건, 스케줄 | + +각 스킬의 `SKILL.md` frontmatter는 에이전트가 언제 해당 스킬을 +로드해야 하는지와 언제 형제 스킬에 위임해야 하는지를 선언합니다. + +## 자주 사용하는 명령 + +```bash +npx skills add / # add a skill bundle from a GitHub repo +npx skills add /@ # pin to a tag / branch / SHA +npx skills list # list installed skills +npx skills remove # remove a skill +npx skills detect # show which agents the CLI will write to +npx skills --help # full options +``` + +재현 가능한 CI를 위해 버전을 고정하세요. + +```bash +npx skills add objectstack-ai/framework@v5.0.0 +``` + +## 출력을 어디에 커밋해야 하나요 + +**커밋하세요.** 스킬은 CI 에이전트를 포함한 팀 전체가 공유해야 하는 +마크다운입니다. CLI는 평소에도 으레 체크인하게 되는 표준 에이전트 +경로(`.claude/`, `.cursor/`, `AGENTS.md`, …)에 기록합니다. + +## 업데이트 + +```bash +npx skills add objectstack-ai/framework # pulls latest matching the spec version range +``` + +`@objectstack/spec`를 업그레이드한 후 다시 실행하면 에이전트가 최신 +Zod 스키마, CEL 헬퍼, 메타데이터 어휘와 동기화된 상태를 유지합니다. + +## 참고 + +- [Build → IDE Skills](/docs/build/ai-skills) — 개념적 개요, AI Builder 대 skills +- [Build → AI Builder](/docs/build/ai-builder) — Console 내 대응 기능 +- [vercel-labs/skills](https://github.com/vercel-labs/skills) — 업스트림 CLI +- [objectstack-ai/framework/skills](https://github.com/objectstack-ai/framework/tree/main/skills) — 모든 스킬의 소스 오브 트루스 diff --git a/content/docs/resources/changelog.ko.mdx b/content/docs/resources/changelog.ko.mdx new file mode 100644 index 0000000..ce52f13 --- /dev/null +++ b/content/docs/resources/changelog.ko.mdx @@ -0,0 +1,98 @@ +--- +title: 변경 로그 및 버전 관리 +description: ObjectOS의 버전 관리 방식, 릴리스 간 변경 사항, 그리고 지원 범위. +--- + +# 변경 로그 및 버전 관리 + +## 버전 관리 정책 + +ObjectOS는 **[유의적 버전(Semantic Versioning)](https://semver.org/)**을 따릅니다: `MAJOR.MINOR.PATCH`. + +| 버전 증가 | 의미 | 해야 할 일 | +|---|---|---| +| **패치** (`4.0.1 → 4.0.2`) | 버그 수정, 동작 변경 없음 | 그대로 업데이트, 앱 변경 불필요 | +| **마이너** (`4.0 → 4.1`) | 새 기능, 하위 호환 | 그대로 업데이트, 필요 시 새 기능 도입 | +| **메이저** (`4 → 5`) | 릴리스 노트에 문서화된 호환성 깨짐 변경 | 업그레이드 전에 마이그레이션 가이드를 읽으세요 | + +모든 `@objectstack/*` 패키지는 동기화된 버전 번호로 함께 릴리스됩니다 — +개별적으로가 아니라 매트릭스로 테스트됩니다. + +## 호환성 매트릭스 + +| 구성 요소 | 호환성 규칙 | +|---|---| +| ObjectOS 이미지 ↔ 컴파일된 아티팩트 | 동일한 마이너 버전. 4.0.x 이미지는 4.0.x 아티팩트를 실행하며, 4.1 아티팩트는 4.0 이미지에서 사용할 수 없는 기능을 사용할 수 있습니다. | +| ObjectOS ↔ CLI | 동일한 마이너 버전 권장. `npm i -g`로 설치한 CLI는 자체 버전에 고정된 스캐폴드를 작성합니다. | +| ObjectOS ↔ 데이터베이스 드라이버 | 드라이버는 이미지 빌드에 고정됨; Postgres ≥ 13 / MongoDB ≥ 5 / Turso(현재 버전) 확인. | +| Node.js | **20 LTS 이상**. 새 배포에는 22 LTS 권장. | + +## 지원 기간 + +| 브랜치 | 상태 | 종료 시점 | +|---|---|---| +| **4.x** (현재) | 활발한 개발; 새 기능 및 수정 | 5.0 출시 후 최소 12개월 | +| **3.x** | 보안 수정만 | 5.0 릴리스 시 EOL | +| **≤ 2.x** | 지원 종료 | 이미 EOL | + +중대한 보안 수정은 현재 및 이전 메이저 버전에 백포트됩니다. 그 외 모든 것은 +`main`에 반영됩니다. + +## 릴리스 노트 + +릴리스된 ObjectOS 버전과 해당 CHANGELOG 항목은 다음에서 게시됩니다: + +- **npm**: [`@objectstack/runtime`](https://www.npmjs.com/package/@objectstack/runtime) +- **GitHub**: [github.com/objectstack-ai/objectos/releases](https://github.com/objectstack-ai/objectos/releases) +- **소스 CHANGELOG**: [`CHANGELOG.md`](https://github.com/objectstack-ai/framework/blob/main/CHANGELOG.md) +- **상세 릴리스 노트**: [`RELEASE_NOTES.md`](https://github.com/objectstack-ai/framework/blob/main/RELEASE_NOTES.md) + +알림을 받으려면 GitHub에서 릴리스를 구독하세요. + +## 주요 변경 사항 + +### 4.0.x — 현재 릴리스 트레인 + +- **42개 패키지**가 `@objectstack/*` 아래 npm에 게시되었습니다. +- Console에 완전한 Configuration 설정 페이지(AI, Storage, + Knowledge)가 추가되었습니다. +- Embedder 서비스: 10개 공급자(`openai`, `azure`, 阿里通义, + 智谱, 硅基流动, 火山, MiniMax, Ollama, custom). +- 실시간 설정 → 런타임 브리지 — Console에서 공급자를 변경하면 재시작 없이 + 프로세스 내에서 교체됩니다. + +### 5.0 (예정) — `project` → `environment` 이름 변경 + +이전에 *Project*라고 불리던 런타임 개념이 전반에 걸쳐 *Environment*로 +이름이 변경됩니다. 영향 범위: + +- CLI 플래그: `--environment` / `-e` +- HTTP 경로: `/api/v1/environments/:environmentId/...` +- 헤더: `X-Environment-Id` +- 환경 변수: `OS_ENVIRONMENT_ID` (`OS_PROJECT_ID`는 4.x에서 별칭으로 유지되며 5.0에서 제거됨) +- DB 컬럼: `environment_id` +- JSON 스키마: `EnvironmentArtifact` + +별칭이나 사용 중단 호환 레이어는 없습니다. 신중하게 업그레이드를 계획하세요 — +릴리스 시 5.0 마이그레이션 가이드를 참고하세요. + +## 업그레이드 + +실제 작업 단계는 [업그레이드 및 롤백](/docs/operate/upgrade)을 참고하세요. +사전 점검: + +1. 현재 버전과 대상 버전 사이의 모든 마이너에 대한 CHANGELOG 항목을 + 읽으세요. +2. `os diff `를 실행하여 호환성을 깨는 스키마 + 변경을 드러내세요. +3. 대상 버전에 대해 `os doctor`를 실행하세요. +4. 전체 플릿에 적용하기 전에 카나리 인스턴스 하나를 먼저 띄우세요. +5. 이미지 태그와 아티팩트 버전 모두에 대한 롤백 계획을 마련하세요(둘은 + 독립적으로 롤백됩니다). + +## 회귀 보고 + +패치 또는 마이너 버전이 이전에 정상 작동하던 것을 망가뜨린 경우, +업그레이드 전/후 버전과 함께 +[github.com/objectstack-ai/objectos/issues](https://github.com/objectstack-ai/objectos/issues)에 +버그를 등록하세요. 회귀는 가장 높은 우선순위의 버그로 처리합니다. diff --git a/content/docs/resources/faq.ko.mdx b/content/docs/resources/faq.ko.mdx new file mode 100644 index 0000000..968fbf6 --- /dev/null +++ b/content/docs/resources/faq.ko.mdx @@ -0,0 +1,187 @@ +--- +title: 자주 묻는 질문 +description: 가장 많이 받는 질문에 대한 답변입니다. +--- + +# 자주 묻는 질문 + +## 시작하기 + +**Q: ObjectOS를 가장 빠르게 사용해 볼 수 있는 방법은 무엇인가요?** +A: `npm i -g @objectstack/cli && os start` — 그런 다음 +http://localhost:3000 을 엽니다. [Quickstart](/docs/quickstart)를 참고하세요. + +**Q: Docker가 필요한가요?** +A: 아니요. Node 20+ 와 CLI만 있으면 충분합니다. Docker는 권장되는 +프로덕션 배포 형태입니다. + +**Q: 데이터베이스가 필요한가요?** +A: 아니요, 시작하는 데는 필요하지 않습니다 — ObjectOS는 기본적으로 로컬 SQLite를 사용합니다. +프로덕션으로 전환할 때 Postgres / MySQL / Turso / Mongo로 교체하세요. + +**Q: 계정 / 클라우드 서비스가 필요한가요?** +A: 아니요. ObjectOS는 완전히 독립적입니다. ObjectStack Cloud는 컨트롤 플레인을 갖춘 +다중 환경 / 다중 앱 배포를 위한 선택 사항입니다. + +## 아키텍처 + +**Q: Postgres / MySQL / MongoDB를 사용할 수 있나요?** +A: 예 — Postgres, MySQL, SQLite, Turso/libSQL, MongoDB가 +지원되는 드라이버입니다. [Runtime Configuration](/docs/configure/runtime)를 참고하세요. + +**Q: Console / Account를 비활성화하고 REST API만 사용할 수 있나요?** +A: 예. `os start --no-ui`를 실행하거나 해당 플래그를 설정하세요. UI가 마운트되어 있든 +아니든 REST API는 동일합니다. + +**Q: Console 대신 제 자체 프런트엔드를 사용할 수 있나요?** +A: 예. Console은 여러분이 자체 코드에서 호출하는 것과 동일한 `/api/v1/*` 엔드포인트를 +사용합니다. `@objectstack/client` SDK 또는 임의의 HTTP 클라이언트를 사용하세요. + +**Q: ObjectOS는 GraphQL을 지원하나요?** +A: REST가 기본 인터페이스입니다. GraphQL은 로드맵에 있습니다 — 그때까지는 +ObjectQL 쿼리 언어(REST `?filter=`/`?sort=` 기반)가 동일한 영역을 +다룹니다. + +**Q: 멀티테넌시는 어떻게 처리되나요?** +A: 하나의 ObjectOS 프로세스는 여러 Environment(테넌트)를 제공할 수 있습니다. 호스트명 +→ Environment 해석은 LRU에 캐시됩니다. 각 Environment는 자체 +데이터베이스, 신원, 감사 로그를 갖습니다. 쿠키는 호스트명별로 스코프가 지정되므로 +세션이 테넌트 간에 누출될 수 없습니다. + +**Q: ObjectOS를 서버리스 / Lambda 환경에서 실행할 수 있나요?** +A: 런타임은 장기 실행되는 Node 프로세스입니다 — 상태 비저장 함수가 아니라 컨테이너나 +VM을 위해 설계되었습니다. 커널 캐시와 Better Auth +세션 모델은 모두 워밍된 인프로세스 상태에 의존합니다. + +**Q: 수평적으로 확장되나요?** +A: 예. 로드 밸런서 뒤에서 여러 인스턴스를 실행하세요. 세션은 +데이터베이스에 저장되므로(메모리가 아님) 어떤 인스턴스든 어떤 요청이든 처리할 수 있습니다. +해당 기능을 활성화하는 경우 공유 속도 제한 및 큐를 위해 Redis를 +사용하세요. + +## 데이터 및 마이그레이션 + +**Q: 스키마 마이그레이션은 어떻게 처리되나요?** +A: 드라이버는 부팅 시 선언된 객체에 맞게 데이터베이스 스키마를 +동기화합니다. Postgres의 경우 `CREATE TABLE` / `ALTER TABLE` 문입니다. +규제 환경에서 통제된 마이그레이션을 수행하려면 +`OS_SKIP_SCHEMA_SYNC=1`을 설정하고 DDL을 직접 관리하세요. + +**Q: 필드 이름을 변경하면 데이터는 어떻게 되나요?** +A: 이름 변경은 데이터 계층에서 파괴적인 변경입니다("기존 열 삭제, +새 열 추가"처럼 보입니다). `os diff`를 사용해 이를 감지하고 마이그레이션 +단계를 추가하세요(새 아티팩트를 배포하기 전에 DB에서 열 이름 +변경). + +**Q: CSV / Excel / Salesforce에서 데이터를 가져올 수 있나요?** +A: CSV: 예, 반복 루프 내에서 `os data create`를 사용하거나 Console 일괄 업로드를 통해 가능합니다. +Salesforce: 현재 가장 좋은 경로는 CSV로 내보낸 후 가져오는 것입니다. 네이티브 +커넥터는 로드맵에 있습니다. + +**Q: ObjectOS를 업그레이드하면 데이터가 손실되나요?** +A: 아니요. 패치 및 마이너 업그레이드는 비파괴적입니다. 메이저 업그레이드 +(예: 4 → 5)는 필요한 마이그레이션을 명시적으로 문서화합니다. 먼저 백업하세요 — +[Backup & DR](/docs/operate/backup). + +## 권한 및 멀티테넌시 + +**Q: 행 수준 보안은 어떻게 하나요?** +A: 공유 규칙(선언적, Salesforce와 유사)을 선언하거나 객체의 `recordAccess` +구성에 CEL 술어를 선언하세요. 보안 플러그인은 +모든 쿼리에 해당 필터를 주입합니다. +[Permissions](/docs/configure/permissions)를 참고하세요. + +**Q: 특정 사용자에게 일부 필드를 보이지 않게 할 수 있나요?** +A: 예 — 권한 집합의 필드 수준 보안입니다. 권한 집합별로 필드별로 +숨김 또는 읽기 전용으로 설정합니다. REST, ObjectQL, +Console 전반에 걸쳐 일관되게 적용됩니다. [Permission Sets](/docs/configure/permissions/permission-sets)를 참고하세요. + +**Q: Okta / Entra / Keycloak를 어떻게 통합하나요?** +A: OIDC. **Console → +Authentication**에서(또는 환경 변수를 통해) 디스커버리 URL + 클라이언트 ID/시크릿을 구성하세요. 제공자 콜백 URL은 +`/api/v1/auth/oauth2/callback/`입니다. [Authentication](/docs/configure/authentication)를 참고하세요. + +## 통합 + +**Q: 웹훅을 보낼 수 있나요?** +A: 예 — `requires`에서 `webhooks`를 활성화하세요. ObjectOS는 HMAC-SHA256 +서명이 적용된 영구 아웃박스를 사용합니다. [Webhooks](/docs/configure/webhooks)를 참고하세요. + +**Q: Zapier / Make / n8n과 통합할 수 있나요?** +A: 예 — 아웃바운드는 웹훅으로, 인바운드는 REST API + API 키로 가능합니다. +인기 있는 iPaaS 도구를 위한 네이티브 커넥터는 로드맵에 있습니다. + +**Q: AI 에이전트가 제 ObjectOS를 호출할 수 있나요?** +A: 예, MCP(`@objectstack/plugin-mcp-server`)를 통해 가능합니다 — 객체와 +액션을 Claude Desktop, IDE 또는 기타 MCP +클라이언트가 사용할 수 있는 MCP 도구로 노출합니다. [AI Service](/docs/configure/ai)를 참고하세요. + +## 커스터마이징 + +**Q: 사용자 정의 플러그인을 작성할 수 있나요?** +A: 예 — 플러그인은 간단한 DI + 라이프사이클 패턴 +(`init → start → destroy`)을 따릅니다. 예제는 GitHub의 `@objectstack/plugin-*` +패키지를 참고하세요. + +**Q: Console의 외관을 커스터마이즈할 수 있나요?** +A: 브랜딩(로고, 강조 색상, 기본 테마)은 **Console → +System Settings**에 있습니다. 심도 있는 UI 커스터마이징은 +`@objectstack/client-react`를 포크하거나 REST API에 맞춰 자체 프런트엔드를 +구축하는 것을 의미합니다. + +**Q: 영어 외의 언어를 추가할 수 있나요?** +A: 예 — i18n은 일급 기능입니다. `os i18n extract` / `os i18n check`를 +사용하고 번역 번들을 배포하세요. + +## 운영 + +**Q: 권장되는 프로덕션 배포는 무엇인가요?** +A: Docker(또는 다중 파드용 Kubernetes) + 관리형 Postgres + 파일용 S3 또는 R2 ++ `OS_AUTH_SECRET`을 위한 시크릿 관리자입니다. +[Production Readiness](/docs/operate/production)를 참고하세요. + +**Q: ObjectOS에 상태 페이지가 있나요?** +A: 셀프 호스팅 배포의 경우 상태는 여러분의 책임입니다 — +`/health`를 모니터에 연결하세요. 호스팅 서비스의 경우 +[status.objectstack.ai](https://status.objectstack.ai)를 참고하세요. + +**Q: 어떤 메트릭을 모니터링해야 하나요?** +A: 5xx 비율, p95 지연 시간, 인증 실패율, 커널 캐시 미스율, +큐 깊이입니다. 최소한의 Prometheus 예제는 [Observability](/docs/operate/observability)에 있습니다. + +**Q: 백업은 어떻게 하나요?** +A: **데이터베이스**와 **스토리지 버킷**을 백업하세요 — 그것들이 +모든 고객 데이터를 담고 있습니다. ObjectOS 자체는 상태 비저장입니다. [Backup](/docs/operate/backup)을 참고하세요. + +## 가격 및 법적 사항 + +**Q: ObjectOS는 정말 무료인가요?** +A: 예. Apache-2.0. 좌석 수, 사용 등급, 라이선스 서버가 없습니다. + +**Q: 제가 판매하는 상용 제품에 ObjectOS를 사용할 수 있나요?** +A: 예. Apache-2.0은 상업적 사용을 허용합니다. [License](/docs/resources/license)를 참고하세요. + +**Q: 텔레메트리를 수집하나요?** +A: 아니요. 여러분이 구성하지 않는 한 아웃바운드 호출은 전혀 없습니다(OIDC, 이메일, AI, +웹훅). [Security & Compliance](/docs/reference/security#data-residency)를 참고하세요. + +**Q: ObjectOS는 SOC 2 / ISO 27001 / HIPAA / GDPR를 준수하나요?** +A: ObjectOS는 모든 프레임워크가 요구하는 **기본 요소**(RBAC, +감사, 암호화 준비, 데이터 위치)를 제공합니다. 인증은 바이너리가 아니라 +여러분의 **배포**의 속성입니다. 많은 ObjectOS 배포가 +인증을 받았습니다. [Security & Compliance](/docs/reference/security#compliance-frameworks)를 참고하세요. + +## 막혔을 때 해결하기 + +**Q: 무언가 망가졌어요 — 어디서부터 시작해야 하나요?** +A: `os doctor`. 이것은 잘못된 구성의 80%를 자체적으로 잡아냅니다. 그 후에는 +[Troubleshooting](/docs/operate/troubleshooting)을 참고하세요. + +**Q: 버그는 어디에 보고하나요?** +A: [GitHub Issues](https://github.com/objectstack-ai/objectos/issues). +`os doctor` 출력을 포함해 주세요. 보안 문제는 +**security@objectstack.ai**로 알려주세요. + +**Q: 사람에게 도움을 받으려면 어디로 가야 하나요?** +A: [GitHub Discussions](https://github.com/objectstack-ai/objectos/discussions), +커뮤니티 Discord, 또는 상업적 지원을 위한 **sales@objectstack.ai**입니다. diff --git a/content/docs/resources/glossary.ko.mdx b/content/docs/resources/glossary.ko.mdx new file mode 100644 index 0000000..ab807b0 --- /dev/null +++ b/content/docs/resources/glossary.ko.mdx @@ -0,0 +1,228 @@ +--- +title: 용어집 +description: ObjectOS와 ObjectStack 전반에서 사용되는 용어 — 각 용어마다 하나의 정의. +--- + +# 용어집 + +이 문서에서 사용되는 각 용어에 대한 단일 표준 정의입니다. + +### Artifact + +컴파일된 `objectstack.json` 파일. 앱에 대한 자체 완결형이며 변경 불가능한 +설명 — 매니페스트, 객체, 뷰, 앱, 플로우, +권한, 번역. `os compile`로 생성됩니다. ObjectOS가 +실제로 실행하는 대상입니다. + +### Action + +메타데이터에 선언된 이름 있는 작업으로, REST +(`/api/v1/data//actions/`), Console 버튼, 또는 플로우 +단계를 통해 호출할 수 있습니다. 호출자의 권한을 상속합니다. + +### Adapter + +ObjectStack를 호스트 런타임과 통합하는 프레임워크 패키지 — +Express, Fastify, Hono, Next.js, Nuxt, SvelteKit, NestJS. 대부분의 +ObjectOS 배포에는 어댑터가 필요하지 않습니다. ObjectOS는 자체 HTTP +서버를 번들로 제공합니다. + +### App + +Console에서 하나의 탐색 가능한 애플리케이션으로 표현되는 객체 + 뷰 + 권한의 +묶음입니다. 하나의 런타임에 여러 앱이 공존할 수 있습니다 +(예: CRM + Helpdesk + Setup). + +### Better Auth + +`@objectstack/plugin-auth`를 구동하는 인증 라이브러리입니다. Better Auth를 +직접 구성하지 않으며, 플러그인이 이를 감쌉니다. + +### Capability + +아티팩트가 자신의 `requires` 목록에 선언하는 선택적 런타임 기능입니다. +패키지에 매핑됩니다 — 예: `audit` → `@objectstack/plugin-audit`. +ObjectOS가 필요에 따라 로드합니다. 다음을 참조하세요: +[Runtime Capabilities](/docs/reference/runtime-capabilities). + +### CEL + +[Common Expression Language](https://github.com/google/cel-spec) — +Google의 안전하고 샌드박스화된 표현식 구문입니다. 수식, +유효성 검사 규칙, 권한 술어, 공유 규칙, 플로우 조건에서 사용됩니다. + +### Console + +`/_console/`의 시스템 UI — 사용자, 역할, 권한 집합, +감사 로그, 세션, API 키, 시스템 설정을 관리합니다. Console +(비즈니스 UI)와 구별됩니다. + +### Control Plane + +버전 관리된 아티팩트를 ObjectOS 인스턴스에 게시하는 선택적 서비스입니다. +ObjectStack Cloud로 호스팅되거나 자체 호스팅됩니다. 대부분의 +배포에는 필요하지 않으며, 파일 기반 모드로 단일 앱 +프로덕션을 운영할 수 있습니다. + +### Driver + +데이터 백엔드 구현체입니다: `driver-sql` (Postgres, MySQL, SQLite, +Turso/libSQL), `driver-mongodb`, `driver-memory`. 부팅 시 데이터베이스 +URL을 통해 선택됩니다. + +### Embedder + +RAG / 시맨틱 검색을 위해 텍스트를 벡터로 변환하는 서비스입니다. +여러 공급자에 걸쳐 교체 가능합니다 (OpenAI, Azure, 硅基流动, Ollama, …). +다음을 참조하세요: [AI Service](/docs/configure/ai). + +### Environment + +자체 데이터베이스와 ID로 뒷받침되는 테넌트별 런타임 인스턴스입니다. +v4.x에서는 때때로 *Project*라고 불렸습니다 (별칭 유지). v5.0은 +CLI, HTTP, 환경 변수, 스키마 전반에서 *Environment*로 표준화합니다. + +### Field + +Object의 형식 지정 속성입니다. 약 25개의 기본 제공 타입: `text`, `select`, +`lookup`, `markdown`, `attachment`, `formula`, `rollup` 등. 다음을 참조하세요: +[Data Model](/docs/build/data-model). + +### Flow + +선언적 비즈니스 로직 — 자동 실행(레코드 트리거), 예약 실행 +(cron), 또는 수동 실행(버튼 / API). 조건/루프/재시도/병렬 +프리미티브를 갖춘 DAG로 실행됩니다. 다음을 참조하세요: +[Flows & Automation](/docs/build/flows). + +### Formula field + +값이 읽기 시점에 평가되는 CEL 표현식인 계산 필드입니다. +저장되지 않습니다. + +### Hook + +객체 수명 주기(`beforeInsert`, `afterUpdate`, …)에 삽입되는 +함수입니다. TypeScript로 작성됩니다. 플로우와는 다릅니다: +훅은 일급 코드이고, 플로우는 메타데이터입니다. + +### Kernel + +플러그인을 로드하고, DI 컨테이너를 보유하며, 이벤트를 디스패치하고, +단일 Environment의 메타데이터를 제공하는 ObjectOS 내부의 마이크로커널입니다. +하나의 프로세스는 LRU 방식으로 여러 캐시된 커널(Environment당 하나)을 +보유할 수 있습니다. + +### Manifest + +아티팩트의 맨 앞에 있는 최상위 메타데이터입니다: `id`, `namespace`, +`version`, `type` (`app` / `plugin` / `service`), `name`, +`description`, `requires` 목록. + +### Marketplace + +설치 가능한 앱의 Console 내 카탈로그입니다. 구성 가능한 +패키지 레지스트리로 뒷받침됩니다. 다음을 참조하세요: [Marketplace](/docs/build/marketplace). + +### MCP (Model Context Protocol) + +AI 에이전트가 도구를 검색하고 호출하기 위한 개방형 프로토콜입니다. +ObjectOS는 `@objectstack/plugin-mcp-server`를 통해 자신의 객체 + 액션을 +MCP로 노출할 수 있습니다. + +### Object + +형식 지정된 비즈니스 엔터티입니다 — `task`, `account`, `invoice`. +TypeScript 스키마로 선언되며, REST API, Console 뷰, 감사 항목, +RBAC 체크포인트를 자동으로 생성합니다. 다음을 참조하세요: [Data Model](/docs/build/data-model). + +### ObjectOS + +런타임 — 앱을 제공하는 단일 Node.js 프로세스입니다. 오픈 +소스, Apache-2.0. **이 문서 사이트는 ObjectOS를 위한 것입니다.** + +### ObjectQL + +데이터 계층 프로토콜 및 쿼리 엔진입니다. 선언적 쿼리를 +네이티브 SQL / Mongo 쿼리로 컴파일합니다. REST 엔드포인트, Console, +플로우에서 사용되며 — 모두 동일한 엔진입니다. + +### ObjectStack + +상위 프로젝트입니다: 프레임워크(`@objectstack/*` npm 패키지), +런타임(ObjectOS), 선택적 클라우드 서비스, 그리고 +marketplace. 때때로 "플랫폼"이라고 불립니다. + +### ObjectUI + +뷰 계층 프로토콜입니다 — 앱, 뷰, 페이지, 대시보드, 액션, +차트, 내비게이션. Console은 ObjectUI 선언을 렌더링합니다. + +### Permission Set + +권한 부여의 묶음입니다 — 객체 권한, 필드 권한, 시스템 +권한. 사용자에게 직접 또는 역할을 통해 연결됩니다. 기본 +권한 부여 단위입니다. 다음을 참조하세요: [Permissions](/docs/configure/permissions). + +### Plugin + +런타임을 하나의 기능으로 확장하는 프레임워크 패키지입니다 — +`plugin-auth`, `plugin-security`, `plugin-audit`, `plugin-webhooks`, +`plugin-mcp-server` 등. DI + 수명 주기 훅 +(`init → start → destroy`)을 통해 활성화됩니다. + +### Project + +**Environment**의 이전 이름입니다. v4.x CLI/환경에서 여전히 사용됩니다(별칭 처리). +v5.0에서 제거되었습니다. + +### Record Share + +특정 사용자 / 역할 / 그룹에 대해 특정 레코드에 대한 접근 권한을 +직접 부여하는 것입니다. `sys_record_share` 행으로 저장됩니다. 공유 +규칙(선언적 기준)과는 다릅니다. + +### Sharing Rule + +기준에 따라 레코드 접근 권한을 부여하는 선언적 규칙입니다 +("지역 관리자는 자신의 지역에 있는 레코드를 볼 수 있다"). 쿼리 +시점에 평가되어 행 수준 필터로 컴파일됩니다. + +### Console + +`/_console/`의 비즈니스 UI — 레코드를 탐색, 생성, 편집하고, +뷰를 구성하며, marketplace에서 앱을 설치합니다. Console(시스템 UI)과 +구별됩니다. + +### Surface + +실행 중인 ObjectOS가 노출하는 네 가지 HTTP 진입점 중 하나입니다: +`/` (REST API), `/_console/`, `/_account/`, `/_console/`. + +### System Context + +보안 검사를 우회해야 하는 플러그인, 훅, 시드 스크립트가 사용하는 +내부 실행 모드입니다. 감사 가능하며, 사용자 코드에는 노출되지 않습니다. + +### Tenant + +멀티테넌트 배포에서의 논리적 격리 경계입니다. 하나의 테넌트는 +일반적으로 하나의 Environment에 매핑됩니다. 쿠키와 세션은 호스트명별로 +범위가 지정되고, 데이터는 Environment별로 범위가 지정됩니다. + +### Trigger + +플로우를 실행하는 조건입니다 — 레코드 이벤트(`after_insert`), +예약(cron), 또는 수동 호출. + +### View + +Object에 연결된 선언적 UI 구성입니다 — 목록, 폼, 칸반, 캘린더, 간트. +Console이 이를 렌더링하며, 컴포넌트를 직접 작성하지 않습니다. + +### Zod schema + +`@objectstack/spec`이 사용하는 런타임 + 컴파일 타임 타입 시스템입니다. +모든 객체, 필드, 뷰, 앱, 플로우는 Zod 스키마로 파싱되고 검증됩니다. +JSON Schema, TypeScript 타입, REST 요청 검증기는 모두 +동일한 Zod 정의에서 파생됩니다. diff --git a/content/docs/resources/license.ko.mdx b/content/docs/resources/license.ko.mdx new file mode 100644 index 0000000..5bdefd7 --- /dev/null +++ b/content/docs/resources/license.ko.mdx @@ -0,0 +1,101 @@ +--- +title: 라이선스 +description: ObjectOS 라이선스 — Apache-2.0, 상용 옵션 및 FAQ. +--- + +# 라이선스 + +## ObjectOS 런타임 — Apache-2.0 + +ObjectOS 런타임과 모든 `@objectstack/*` 오픈소스 패키지는 +[Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)에 따라 라이선스가 부여됩니다. + +쉽게 말하면 다음과 같습니다. + +| 할 수 있는 것 | 해야 하는 것 | +|---|---| +| 상용 제품에 사용 | 재배포 시 LICENSE 및 NOTICE 파일 포함 | +| 변경 사항을 공유하지 않고 수정 | 수정된 파일을 수정됨으로 표시 | +| 독점 소프트웨어에 임베드 | 허가 없이 ObjectStack 상표를 사용하지 않음 | +| 어떤 목적으로든, 어디서든 실행 | — | +| 더 큰 저작물의 일부로 서브라이선스 | — | + +**카피레프트는 없습니다.** 내부용으로 수행한 수정이나 +폐쇄형 소스 제품에 포함하여 출시한 수정 사항은 다시 기여할 +필요가 **없습니다**. Apache-2.0 라이선스에는 기여자로부터의 +**명시적 특허 권리 부여**도 포함되어 있어 엔터프라이즈 법무 검토에 +중요합니다. + +전문: [LICENSE](https://github.com/objectstack-ai/framework/blob/main/LICENSE) + +## Apache-2.0에 포함되는 항목 + +| | 라이선스 | +|---|---| +| ObjectOS 런타임 이미지 | Apache-2.0 | +| 모든 `@objectstack/*` npm 패키지 (런타임, 플러그인, 서비스, 드라이버, 어댑터, CLI, 클라이언트 SDK) | Apache-2.0 | +| 템플릿 저장소 (`objectstack-ai/templates`) | Apache-2.0 | +| Console 및 Account UI | Apache-2.0 | +| 문서 | CC-BY-4.0 | + +## 상용 계약이 필요할 수 있는 경우 + +Apache-2.0은 대부분의 사용 사례를 다룹니다. 다음과 같은 경우에는 +상용 계약이 필요할 수 있습니다. + +| 시나리오 | 제공 항목 | +|---|---| +| **SLA가 포함된 지원**이 필요한 경우 | 상용 지원 | +| 우리가 **대신 호스팅**해 주기를 원하는 경우 | 관리형 클라우드 (ObjectStack Cloud) | +| **OEM 권리**(ObjectStack 상표 사용, 자체 제품으로 배지 표시)를 원하는 경우 | OEM 계약 | +| **보증 / 면책**을 원하는 경우 | 상용 계약 (Apache-2.0은 보증을 부인함) | +| **규제 증명**(SOC 2 보고서, 런타임 이미지의 ISO 인증서)이 필요한 경우 | 엔터프라이즈 등급 | + +논의를 원하시면 **sales@objectstack.ai**로 문의하세요. + +## 오픈소스 런타임의 가격 + +**무료입니다.** 영원히. 좌석도, 사용량 등급도, 라이선스 서버도, 키도 없습니다. + +다음에서 실행하세요. +- 노트북 +- 서버 +- 고객의 서버 +- Raspberry Pi +- 파드 1000개 규모의 Kubernetes 클러스터 + +라이선스는 모든 경우에 동일합니다. + +## 서드파티 구성 요소 + +ObjectOS는 여러 업스트림 오픈소스 프로젝트(Node.js, +Hono, Zod, Better Auth, AI SDK, …)에 의존합니다. 각 프로젝트는 +자체 라이선스와 함께 배포되며, 모두 Apache-2.0, MIT 또는 BSD 스타일의 +허용적 라이선스로 상용 사용과 호환됩니다. 전체 목록은 각 릴리스와 함께 +게시되는 SBOM을 참조하세요. + +## FAQ + +**Q: 고객에게 판매하는 폐쇄형 소스 SaaS에서 ObjectOS를 사용할 수 있나요?** +A: 네. Apache-2.0에는 카피레프트가 없습니다. 코드를 공유할 필요가 없습니다. + +**Q: ObjectOS를 수정하고 변경 사항을 공유하지 않아도 되나요?** +A: 네. Apache-2.0은 수정 사항 공유를 요구하지 않습니다. + +**Q: 제 제품의 일부로 Console을 리브랜딩할 수 있나요?** +A: UI 자체는 가능합니다. 제품을 홍보하기 위해 "ObjectStack" 이름이나 +로고를 사용하려면 OEM 계약이 필요합니다 — 영업팀에 문의하세요. + +**Q: 제 제품이 수익을 내면 비용을 지불해야 하나요?** +A: 아니요. 로열티나 수익 분배가 없습니다. + +**Q: ObjectOS가 사용 데이터를 본사로 전송하나요?** +A: 아니요. 텔레메트리, 라이선스 검사, 업데이트 핑이 없습니다. +[Security & Compliance](/docs/reference/security#data-residency)를 참조하세요. + +**Q: 계약상의 보증을 원하면 어떻게 하나요?** +A: Apache-2.0은 보증을 부인합니다. 상용 계약을 구매하면 보증을 +제공해 드립니다. + +**Q: 오픈소스 버전은 항상 무료인가요?** +A: 네. Apache-2.0 권리 부여는 철회할 수 없습니다. diff --git a/content/docs/resources/meta.ko.json b/content/docs/resources/meta.ko.json new file mode 100644 index 0000000..fc6c5e8 --- /dev/null +++ b/content/docs/resources/meta.ko.json @@ -0,0 +1,11 @@ +{ + "title": "리소스", + "defaultOpen": false, + "pages": [ + "glossary", + "faq", + "changelog", + "license", + "support" + ] +} diff --git a/content/docs/resources/support.ko.mdx b/content/docs/resources/support.ko.mdx new file mode 100644 index 0000000..222dcd0 --- /dev/null +++ b/content/docs/resources/support.ko.mdx @@ -0,0 +1,78 @@ +--- +title: 지원 +description: 도움을 받을 수 있는 곳, 버그 신고 방법, 응답 기대치. +--- + +# 지원 + +## 상황별 안내 + +| 다음과 같은 경우 … | 이동할 곳 | +|---|---| +| "어떻게 하나요" 질문이 있는 경우 | [GitHub Discussions](https://github.com/objectstack-ai/objectos/discussions) — 커뮤니티 + 메인테이너 | +| 버그를 발견한 경우 | [GitHub Issues](https://github.com/objectstack-ai/objectos/issues) — 버전, 재현 방법, 예상 동작 대 실제 동작을 포함해 주세요 | +| 보안 취약점을 발견한 경우 | **security@objectstack.ai** — 공개 이슈를 **열지** 마세요 | +| 상용 지원 / SLA가 필요한 경우 | **sales@objectstack.ai** | +| 사용자 + 메인테이너와 대화하고 싶은 경우 | [Discord](https://discord.gg/objectstack) (커뮤니티 운영) | +| 릴리스를 팔로우하고 싶은 경우 | [github.com/objectstack-ai/objectos](https://github.com/objectstack-ai/objectos) → Releases를 Watch | +| 유료로 기능 구현을 원하는 경우 | Discussion을 열고 → `funding` 태그를 추가 | + +## 좋은 버그 리포트 작성하기 + +30초 버전: + +```text +ObjectOS version: 4.0.4 (or sha-abc123) +Node version: 22.4.0 +Install method: docker / npm -g / source +Database: postgres 16 / sqlite / turso / mongo +Repro steps: 1. ... 2. ... 3. ... +Expected: ... +Actual: ... +Logs: (sanitized, last 30 lines) +``` + +구성 관련 문제의 경우 `os doctor`의 출력을 첨부하세요. 이 명령은 +잘못된 구성의 80%를 자체적으로 진단합니다. + +## 응답 기대치 + +| 채널 | 최선의 노력 기준 응답 시간 | +|---|---| +| 보안 이메일 | 영업일 기준 1일 | +| GitHub Issues — 버그, 재현 방법 포함 | 분류까지 영업일 기준 3일, 다음 릴리스 주기에 수정 | +| GitHub Issues — 기능 요청 | 분류까지 영업일 기준 7일 | +| GitHub Discussions | 커뮤니티 주도; 메인테이너가 정기적으로 참여 | +| 상용 지원 | 지원 계약에 따름 | + +이는 최선의 노력 기준이며 SLA가 아닙니다. 상용 지원 계약에는 +공식 SLA가 포함됩니다. + +## 상용 지원 및 서비스 + +ObjectStack 팀에서 제공: + +- 응답 시간 SLA가 포함된 **상용 지원**. +- **매니지드 호스팅** — 저희가 운영해 드립니다. +- **아키텍처 검토** — 배포 전 점검. +- 사양에 맞춰 제작된 **맞춤형 플러그인 / 기능**. +- **OEM 재배포** — ObjectOS를 제품에 임베드. + +**sales@objectstack.ai**로 문의하세요. + +## 기여하기 + +ObjectOS는 Apache-2.0이며 기여를 받습니다: + +- [CONTRIBUTING.md](https://github.com/objectstack-ai/framework/blob/main/CONTRIBUTING.md) +- [Code of Conduct](https://github.com/objectstack-ai/framework/blob/main/CODE_OF_CONDUCT.md) +- 초안 PR을 일찍 여세요 — 중복 작업이 발생하는 것보다 스케치를 + 검토하는 편이 낫습니다. + +## 상태 및 장애 + +호스팅 서비스(ObjectStack Cloud, 패키지 레지스트리): +[status.objectstack.ai](https://status.objectstack.ai). + +자체 호스팅된 ObjectOS 배포의 경우 상태는 사용자의 책임입니다 — +평소 사용하는 모니터링에 연결하세요(자세한 내용은 [Observability](/docs/operate/observability) 참조). diff --git a/content/docs/why.ko.mdx b/content/docs/why.ko.mdx new file mode 100644 index 0000000..811d5bb --- /dev/null +++ b/content/docs/why.ko.mdx @@ -0,0 +1,141 @@ +--- +title: 왜 ObjectOS인가 +description: 솔직한 제안 — 언제 사용해야 하고, 언제 사용하지 말아야 하며, 무엇이 다른지. +--- + +# 왜 ObjectOS인가 + +이 페이지는 ObjectOS가 여러분에게 적합한지 알아내기 위해 다른 모든 문서의 +행간을 읽을 필요가 없도록 존재합니다. + +## 이 베팅의 형태 + +ObjectOS는 하나의 확고한 베팅을 합니다. **AI가 애플리케이션의 메타데이터를 +작성하고, 여러분은 그것을 실행하는 런타임을 소유합니다.** + +여러분은 객체, 필드, 뷰, 플로우, 권한을 파일 하나하나 직접 손으로 작성하지 +않습니다. 사용자(또는 그들을 대신해 작동하는 AI 에이전트)가 필요한 것을 +Console 내장 [AI Builder](/docs/build/ai-builder)에게 평이한 언어로 +설명합니다. 그러면 AI Builder는 감사된 소규모 도구 집합을 호출하고, 모든 +변경 사항을 사람의 승인을 위해 대기열에 넣으며, 그 결과는 곧바로 작동합니다 — +REST 엔드포인트, Console 화면, RBAC, 감사 로그, 이 모든 것이 동일한 +메타데이터에서 생성됩니다. + +런타임은 **여러분의** VPC, **여러분의** 데이터베이스, **여러분의** +Apache-2.0 포크 위에 자리합니다. 모델은 여러분의 데이터 웨어하우스가 아니라 +샌드박스화된 메타데이터 API와 대화합니다. + +이것이 제안의 전부입니다. 이 페이지의 나머지는 누구에게 맞고 누구에게 +맞지 않는지에 관한 것입니다. + +## 다음과 같다면 ObjectOS를 사용하세요 … + +- 내부 도구, 관리자 패널 또는 백오피스 앱이 필요하고, **또한** +- 그것을 사용하는 사람들(또는 그들을 대신해 작동하는 AI 에이전트)이 티켓을 + 발행하지 않고도 그것을 *확장*할 수 있기를 원하며, **또한** +- 데이터를 다른 사람의 클라우드에 둘 수 없거나 두고 싶지 않고, **또한** +- 인증 + RBAC + 감사 + 파일 업로드 + 작업 + 웹훅을 열 번째로 다시 만들고 + 싶지 않다면. + +적합한 일반적인 시나리오: + +| 시나리오 | 왜 효과적인가 | +|---|---| +| 보안 검토에서 데이터 주권이 거론되어 Retool / Appsmith 앱을 대체할 때 | ObjectOS는 여러분의 VPC에서 실행되며, 데이터는 절대 외부로 나가지 않습니다 | +| 규제 대상 비즈니스를 위한 컴플라이언스 / 리스크 / 벤더 관리 도구를 구축할 때 | 감사 로그, RBAC, 필드 보안, 행 수준 격리가 기본 기능이며 — 모든 AI 주도 변경 자체가 하나의 감사 항목입니다 | +| SaaS 제품을 위한 내부 관리자를 구축할 때 | 단일 Node 프로세스로, 기존 서비스 옆에 그대로 들어맞습니다 | +| 엔터프라이즈 고객을 위한 에어갭 또는 온프레미스 배포 | 기본 배포 대상이며, 인터넷 송신이 필요 없습니다(로컬 모델 직접 준비) | +| 멀티 테넌트 내부 포털(하나의 런타임, 여러 개의 작은 앱) | 프로젝트별 커널 + LRU 캐시가 이를 위해 설계되었습니다 | +| 사용자가 자신만의 확장을 안전하게 "바이브 코딩"하기를 원할 때 | AI Builder + HITL 승인 대기열 + 감사 로그가 바로 그 핵심입니다 | + +## 다음과 같다면 ObjectOS를 사용하지 마세요 … + +- 트래픽이 많은 소비자 제품을 구축하고 있다면 → 전통적인 웹 프레임워크를 + 사용하세요, 더 많은 제어권을 갖게 됩니다. +- 최종 사용자를 위한 픽셀 단위로 완벽한 맞춤형 UI가 필요하다면 → ObjectOS의 + Console은 관리자/내부용입니다. REST를 통해 여러분만의 프런트엔드와 함께 + 사용하세요. +- 비엔지니어를 위한 노코드 드래그 앤 드롭 빌더와 호스팅된 클라우드를 + 원한다면 → Retool, Bubble 또는 Airtable을 사용하세요. ObjectOS는 + 코드 우선, AI 주도, 자체 호스팅 방식입니다. +- 실시간 협업 편집(Figma 스타일)이 필요하다면 → 그것은 realtime 플러그인이 + 해결하는 문제가 아닙니다. + +## 여러분이 아마 사용 중인 것과의 비교 + +### vs. Retool / Appsmith / Internal + +| | Retool | ObjectOS | +|---|---|---| +| 데이터 위치 | 그들의 클라우드(또는 상위 등급에서 자체 호스팅) | 항상 여러분의 네트워크 | +| UI 빌더 | 드래그 앤 드롭, 매우 세련됨 | 메타데이터 기반, 생성됨; 덜 맞춤화됨 | +| 가격 | 사용자당, 고통스럽게 증가 | 자체 호스팅, Apache-2.0 | +| 워크플로우 / 트리거 | 그들의 워크플로우 엔진 | 선언적 플로우 + 플러그인 | +| 백엔드 로직 | 그들의 쿼리 편집기로 제한됨 | 전체 TypeScript, 전체 Node 생태계 | +| 가장 적합한 용도 | 기존 API 위에 빠른 대시보드 | 데이터를 *소유*하는 앱 | + +### vs. Supabase / Firebase + +| | Supabase | ObjectOS | +|---|---|---| +| 설정 시간 | 약 30초 | 약 30초 | +| 데이터베이스 | Postgres, 그들의 것(자체 호스팅 가능) | 모든 Postgres / MySQL / SQLite / Turso / Mongo, **여러분의 것** | +| 인증 | 내장 | 내장 | +| 생성된 API | PostgREST | ObjectQL 생성 REST | +| 관리자 UI | Console(기본) | Console + Account | +| RBAC | Postgres RLS를 통한 행 수준 | RBAC + 행 수준 + 필드 수준, 선언적 | +| 감사 로그 | 직접 구현 | 기본 기능 | +| 벤더 종속 | 그들의 인증 + 그들의 스토리지 + 그들의 realtime | 없음 — 모든 계층이 플러그인 | +| 가장 적합한 용도 | BaaS를 원하는 신규 앱 | 런타임을 소유해야 하는 앱 | + +### vs. Salesforce / NetSuite / ServiceNow + +| | Salesforce | ObjectOS | +|---|---|---| +| 데이터 모델 | 객체 + 필드 + 관계 | 동일 | +| 권한 | 프로필 + 권한 집합 + 공유 규칙 + FLS | **동일한 어휘**, 선언적 TypeScript | +| 사용자당 비용 | 사용자당 월 $150-300 | $0 | +| 커스터마이징 | Apex + Lightning + 플로우 | TypeScript + 플로우 | +| 실행 위치 | 오직 그들의 클라우드 | 여러분의 인프라 | +| "우리가 소유"하기까지의 시간 | 수개월의 컨설턴트 작업 | 하루 오후 | +| 가장 적합한 용도 | 생태계에 비용을 지불할 영업 주도 기업 | 세금 없이 모델을 원하는 팀 | + +### vs. 직접 구축(Next.js + Prisma + NextAuth) + +| | DIY | ObjectOS | +|---|---|---| +| 첫 REST 엔드포인트 | 몇 시간 | 60초 | +| 인증(이메일 + OAuth + OIDC + 패스키 + 2FA) | 수 주 | 포함됨 | +| 모든 객체를 위한 관리자 UI | 객체마다 구축 | 생성됨 | +| 감사 로그 | 직접 구현 | 플러그인, 선언적 | +| 권한이 적용된 S3 파일 업로드 | 직접 구현 | 플러그인 | +| 백그라운드 작업 + 재시도 + 데드 레터 | 직접 구현 | 플러그인 | +| 멀티 테넌시 | 직접 구현(그리고 두 번은 잘못 만들 것입니다) | 내장 | +| 가장 적합한 용도 | 맞춤형 UX를 가진 공개 앱 | 속도가 우선인 내부 도구 | + +## 솔직한 트레이드오프 + +- **Retool보다 UI 자유도가 낮습니다.** Console은 여러분의 메타데이터에서 + 생성됩니다. 맞춤형 프런트엔드와 함께 사용할 수 있지만(REST API는 Console이 + 사용하는 것과 동일합니다), 손수 만든 픽셀 단위로 완벽한 UI가 필요하다면 + 직접 구축하고 ObjectOS를 백엔드로 사용하세요. +- **TypeScript 우선.** 비엔지니어가 객체를 직접 작성하지는 않습니다. + Salesforce 관리자는 UI 빌더를 클릭하는 데 익숙하지만, 여기서는 `git`입니다. +- **Salesforce보다 신생.** Salesforce는 25년간 문서화된 엣지 케이스를 + 가지고 있습니다. 우리는 수백 개를 가지고 있습니다. 프로토콜은 안정적이며, + 생태계는 성장 중입니다. +- **Apache-2.0.** 상업용 제품에 사용하고, 임베드하고, 비공개로 수정하세요. + 카피레프트의 깜짝 놀랄 일은 없습니다. 선택적 상업 지원은 별도로 + 제공됩니다. + +## 현실 점검 + +가장 작은 실용적인 ObjectOS 배포는 단일 `pnpm dev` 또는 SQLite를 사용하는 +단일 Docker 컨테이너입니다. 오늘날 프로덕션에서 가장 큰 배포는 Postgres + +S3 + Redis로 여러 지역에 걸쳐 수만 명의 내부 사용자를 서비스합니다. 둘 다 +동일한 소프트웨어입니다. + +`npx @objectstack/cli init my-app`으로 시작해서 5분 안에 결정하세요. 여러분에게 +맞지 않는다면 5분을 쓴 것뿐입니다. + +[**Quickstart 사용해보기 →**](/docs/quickstart)