Pullboard
git 저장소에 존재하는 작업 보드로, 에이전트가 작업을 자동으로 할당·검토하고 별도 설명이 필요 없는 로컬 오픈소스 도구
Stack, OpenSaas 제공: 설정 중심 Next.js 프레임워크로 모든 데이터베이스 작업에 접근 제어 적용
Stack, by OpenSaas: a config-first Next.js framework with access control enforced on every database operation — so features built by AI coding agents are secure by construction.
개발 도구TypeScript · JavaScriptGitHub @OpenSaasAU온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Beta. The onboarding path and core APIs are stable enough to build on; some advanced features are still evolving. Pin versions for production.
A config-first stack for building admin-heavy applications with Next.js App Router — designed so you can describe features to an AI coding agent (Claude Code) and let the framework handle the technical complexity, with access control enforced on every database operation by default.
context.db operation is automatically secured; denied reads return null/[] (no information leakage).CLAUDE.md and MCP tooling, so you build by describing features.You need Node.js 18+ and pnpm (npm install -g pnpm).
# 1. Scaffold — installs deps and generates the schema
npm create opensaas-app@latest my-app
# 2. Run — starts the Dev database, applies the schema, and runs the app
cd my-app
pnpm dev
3. Build with Claude Code. Open the project in Claude Code and describe what you want — e.g. "add a comments feature to posts" or "add authentication". The scaffolded project ships a CLAUDE.md and MCP tooling so Claude builds within the framework's guardrails.
Your app runs at http://localhost:3000; the auto-generated admin UI is at /admin.
Want authentication from the start?
npm create opensaas-app@latest my-app --with-auth
The scaffolder runs install → generate for you. To skip that and run the steps yourself, pass
--no-install.
📚 Full documentation → · Quick Start guide → · Building with Claude Code →
opensaas.config.tsimport { config, list } from '@opensaas/stack-core'
import { text, select, relationship } from '@opensaas/stack-core/fields'
import type { AccessControl } from '@opensaas/stack-core'
// Access control returns a boolean, or a Where filter that scopes the rows.
const isAuthor: AccessControl = ({ session }) =>
session ? { authorId: { equals: session.userId } } : false
export default config({
db: { provider: 'postgresql' },
lists: {
Post: list({
fields: {
title: text({ validation: { isRequired: true } }),
content: text(),
status: select({
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
defaultValue: 'draft',
}),
author: relationship({ ref: 'User.posts' }),
},
access: {
operation: {
// Anonymous users only see published posts; authors see their own drafts too.
query: ({ session }) => (session ? true : { status: { equals: 'published' } }),
update: isAuthor,
delete: isAuthor,
},
},
}),
User: list({
fields: {
name: text({ validation: { isRequired: true } }),
email: text({ isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
},
})
pnpm generate # → prisma/contract.ts, prisma/contract.json, .opensaas/types.ts, .opensaas/context.ts
pnpm dev runs this for you on every start and on every edit to opensaas.config.ts — there is no separate database step to run first.
import { getContext } from '@/.opensaas/context'
export async function getPosts() {
const context = await getContext() // pass a session for authenticated access
// Access control is enforced automatically.
return context.db.Post.include('author', (author) => author.select('name')).all()
}
Denied operations return null (single) or [] (many) rather than throwing, so callers can't distinguish "denied" from "doesn't exist". Always null-check writes:
const post = await context.db.Post.update({ where: { id }, data })
if (!post) return { error: 'Not found or access denied' }
Packages
| Package | Purpose |
|---|---|
@opensaas/stack-core | Config system, fields, access-control engine, hooks, generators (MCP runtime via @opensaas/stack-core/mcp) |
@opensaas/stack-cli | opensaas generate / dev and the migration tooling |
@opensaas/stack-ui | Composable admin UI — primitives, field components, standalone CRUD, full admin |
@opensaas/stack-auth | Better-auth integration (sessions, OAuth, MCP adapter via @opensaas/stack-auth/mcp) |
@opensaas/stack-tiptap | Rich-text richText() field (third-party field example) |
@opensaas/stack-rag | Semantic search with vector embeddings |
@opensaas/stack-storage (+ -s3, -vercel) | File/image uploads with pluggable storage adapters |
create-opensaas-app | npm create opensaas-app project scaffolder |
Examples live in examples/: starter, starter-auth, blog, auth-demo, composable-dashboard, custom-field, json-demo, file-upload-demo, mcp-demo, rag-ollama-demo, rag-openai-chatbot, tiptap-demo.
Choose your level of abstraction:
import { Button, Card } from '@opensaas/stack-ui/primitives' // 1. shadcn/ui primitives
import { TextField, SelectField } from '@opensaas/stack-ui/fields' // 2. field components
import { ItemCreateForm, ListTable } from '@opensaas/stack-ui/standalone' // 3. standalone CRUD
import { AdminUI } from '@opensaas/stack-ui' // 4. full admin UI
See examples/composable-dashboard and the UI docs.
npx @opensaas/stack-cli migrate to get started)prisma migrateWorking on OpenSaas Stack itself (not building an app with it)? See CONTRIBUTING.md for monorepo setup, building packages, running tests, and the changeset/plugin-version workflow.
MIT