{"id":47801016,"url":"https://github.com/placet-io/placet","last_synced_at":"2026-04-22T23:06:52.186Z","repository":{"id":348127755,"uuid":"1186681871","full_name":"placet-io/placet","owner":"placet-io","description":"Placet is a Modular HITL Agent communication platform build with flexibility and independence in mind","archived":false,"fork":false,"pushed_at":"2026-03-31T19:08:46.000Z","size":3077,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-31T21:14:47.782Z","etag":null,"topics":["agent-orchestration","ai","ai-agents","automation","hitl","human-in-the-loop","nocode","workflow-tool"],"latest_commit_sha":null,"homepage":"https://placet.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/placet-io.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-19T22:07:18.000Z","updated_at":"2026-03-31T19:08:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/placet-io/placet","commit_stats":null,"previous_names":["centerbitco/placet","placet-io/placet"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/placet-io/placet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/placet-io%2Fplacet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/placet-io%2Fplacet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/placet-io%2Fplacet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/placet-io%2Fplacet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/placet-io","download_url":"https://codeload.github.com/placet-io/placet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/placet-io%2Fplacet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31364577,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T15:19:21.178Z","status":"ssl_error","status_checked_at":"2026-04-03T15:19:20.670Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-orchestration","ai","ai-agents","automation","hitl","human-in-the-loop","nocode","workflow-tool"],"created_at":"2026-04-03T17:00:52.447Z","updated_at":"2026-04-03T17:01:03.276Z","avatar_url":"https://github.com/placet-io.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"apps/frontend/public/logo-readme.svg\" alt=\"Placet\" width=\"80\" height=\"80\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003ePlacet\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eThe inbox where AI agents meet humans.\u003c/strong\u003e\u003cbr/\u003e\n  A self-hostable, plugin-driven platform for Human-in-the-Loop workflows.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Self--Hostable-2496ED?style=for-the-badge\u0026logo=docker\u0026logoColor=white\" alt=\"Self-Hostable\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Plugin%20System-8B5CF6?style=for-the-badge\u0026logo=puzzle-piece\u0026logoColor=white\" alt=\"Plugin System\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Real--Time-10B981?style=for-the-badge\u0026logo=socketdotio\u0026logoColor=white\" alt=\"Real-Time\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/REST%20API-F97316?style=for-the-badge\u0026logo=swagger\u0026logoColor=white\" alt=\"REST API\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Human--in--the--Loop-3B82F6?style=for-the-badge\u0026logo=users\u0026logoColor=white\" alt=\"Human-in-the-Loop\" /\u003e\n  \u003ca href=\"https://docs.placet.io\"\u003e\u003cimg src=\"https://img.shields.io/badge/Docs-docs.placet.io-6366F1?style=for-the-badge\" alt=\"Documentation\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/NestJS-E0234E?logo=nestjs\u0026logoColor=white\" alt=\"NestJS\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Next.js-000000?logo=nextdotjs\u0026logoColor=white\" alt=\"Next.js\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Prisma-2D3748?logo=prisma\u0026logoColor=white\" alt=\"Prisma\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/PostgreSQL-4169E1?logo=postgresql\u0026logoColor=white\" alt=\"PostgreSQL\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Docker-2496ED?logo=docker\u0026logoColor=white\" alt=\"Docker\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TypeScript-3178C6?logo=typescript\u0026logoColor=white\" alt=\"TypeScript\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Turborepo-0F0F0F?logo=turborepo\u0026logoColor=white\" alt=\"Turborepo\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Tailwind%20CSS-06B6D4?logo=tailwindcss\u0026logoColor=white\" alt=\"Tailwind CSS\" /\u003e\n\u003c/p\u003e\n\n---\n\n## Why Placet?\n\nAI agents are getting more capable every day, but they still need humans in the loop. Approvals, reviews, feedback, complex decisions. Today, most teams wire this through Slack, Teams, or email (tools designed for human-to-human chat, not human-to-agent collaboration).\n\n**The problem:**\n\n- Messenger apps aren't built for structured agent interactions. An approval button in Slack is a hack, not a feature.\n- AI can do much more than send text messages. Agents generate images, diagrams, documents, and code, but there's no good way to review and respond to rich content inline.\n- Reviewing files (images, PDFs, documents) requires switching to external services. Annotations, comparisons, and approvals happen outside the conversation.\n- Every integration requires custom glue code. There's no standard way for agents to present custom UIs.\n\n**Placet is different:**\n\n- **Purpose-built for Human-in-the-Loop.** Not a chat app with agent integrations bolted on. The entire platform is designed around the agent-human interaction loop.\n- **Rich message types out of the box.** Agents send structured reviews (approval buttons, forms, selections, free text), files with inline preview, and custom plugin UIs, all rendered natively in the chat.\n- **Everything reviewable in one place.** Images, PDFs, video, documents, and spreadsheets are all viewable and annotatable directly in the conversation. No context switching.\n- **Plugin system for unlimited flexibility.** Two files (`plugin.json` + `render.html`) = a custom message type. Build CRM cards, diagram renderers, diff viewers, or whatever your workflow needs. Plugins run in sandboxed iframes with a full Bridge API.\n- **Multiple communication channels.** WebSocket for real-time, webhooks for async delivery, long-polling for synchronous agents. Your agent connects however it wants.\n- **Self-hostable, no vendor lock-in.** One `docker compose up` and you're running. No cloud dependency, no LLM provider coupling. Placet connects to _your_ systems, not the other way around.\n\n---\n\n## Quickstart\n\n\u003e **Prerequisites:** Git, Node.js 22+, Docker \u0026 Docker Compose\n\n```bash\ngit clone https://github.com/placet-io/placet.git\ncd placet\ncp .env.example .env\nmake setup\n```\n\nThat's it. `make setup` installs dependencies, builds packages, starts all Docker services (Postgres, MinIO, Backend, Frontend), runs migrations, and creates the initial user.\n\n| Service           | URL                            |\n| ----------------- | ------------------------------ |\n| **Frontend**      | http://localhost:3000          |\n| **Backend API**   | http://localhost:3001          |\n| **Swagger Docs**  | http://localhost:3001/api/docs |\n| **MinIO Console** | http://localhost:9001          |\n\n**Default login:** `admin@placet.local` / `changeme` (configurable in `.env`)\n\n### Send your first agent message\n\n1. Go to **Settings → API Keys** and create a key\n2. Go to **Settings → Agents** and create an agent\n3. Send a message:\n\n```bash\ncurl -X POST http://localhost:3001/api/v1/messages \\\n  -H \"x-api-key: hp_your-key-here\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"text\": \"Hello from my agent!\", \"status\": \"success\"}'\n```\n\n4. Open the agent's chat and your message appears in real-time.\n\n### Request human approval\n\n```bash\ncurl -X POST http://localhost:3001/api/v1/messages \\\n  -H \"x-api-key: hp_your-key-here\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"text\": \"Deploy v2.1 to production?\",\n    \"review\": {\n      \"type\": \"approval\",\n      \"payload\": {\n        \"options\": [\n          {\"id\": \"deploy\", \"label\": \"Deploy\", \"style\": \"primary\"},\n          {\"id\": \"cancel\", \"label\": \"Cancel\", \"style\": \"danger\"}\n        ]\n      }\n    }\n  }'\n```\n\n---\n\n## Features\n\n### Agent Communication\n\n| Feature             | Description                                                                                        |\n| ------------------- | -------------------------------------------------------------------------------------------------- |\n| **Push API**        | Agents send messages via `POST /api/v1/messages` (text, status, attachments, reviews, and plugins) |\n| **Chat-as-Storage** | Agents query their own chat history with search, filters, and cursor pagination                    |\n| **Agent Status**    | Heartbeat endpoint with 4 states (active, busy, error, offline) and status history                 |\n| **WebSocket**       | Real-time delivery via Socket.io (messages, reviews, and agent status)                             |\n| **Webhooks**        | Outbound delivery on review responses with tiered priority and SSRF protection                     |\n| **Long-polling**    | Synchronous wait for review responses (30s timeout)                                                |\n| **Web Push**        | Browser push notifications via Service Worker + VAPID                                              |\n\n### Review System\n\n| Type           | Agent sends                                | User sees                              |\n| -------------- | ------------------------------------------ | -------------------------------------- |\n| **Approval**   | `options: [{id, label, style}]`            | Styled buttons with optional comment   |\n| **Selection**  | `mode: \"single\"\\|\"multi\", items: [...]`    | Radio buttons or checkboxes            |\n| **Form**       | `fields: [{name, type, label, required?}]` | Dynamic form with validation           |\n| **Text Input** | `placeholder?, markdown?`                  | Textarea with markdown preview         |\n| **Freeform**   | `{}`                                       | Generic JSON response (for plugin UIs) |\n\nReviews support optional expiry, webhook callbacks, and can be combined with plugin messages.\n\n### File Handling\n\n| Feature                 | Supported Formats                          |\n| ----------------------- | ------------------------------------------ |\n| **Image viewer**        | JPG, PNG, GIF, WebP, SVG                   |\n| **PDF viewer**          | PDF (browser-native)                       |\n| **Video player**        | MP4, WebM                                  |\n| **Audio player**        | MP3, WAV, OGG                              |\n| **Document viewer**     | DOCX                                       |\n| **Spreadsheet viewer**  | XLSX, CSV                                  |\n| **Presentation viewer** | PPTX (text extraction)                     |\n| **Code/text viewer**    | Any text file with syntax highlighting     |\n| **Canvas annotation**   | Pen, arrow, rectangle, text — on any image |\n| **Bulk download**       | ZIP archive of selected files              |\n| **Share links**         | JWT-based unauthenticated download links   |\n\n### Plugin System\n\nTwo files = a custom message type. No build step required.\n\n```\npackages/plugins/my-plugin/\n  plugin.json      ← Manifest (metadata, input schema, permissions, env)\n  render.html      ← HTML + CSS + JS rendered in sandboxed iframe\n```\n\nPlugins get a `Placet` global with a full Bridge API:\n\n```javascript\nPlacet.data; // Plugin input data from the agent\nPlacet.env; // Configured environment variables\nPlacet.attachments; // Attached files\nPlacet.message; // Message context (id, channelId, senderType)\nPlacet.theme; // 'light' or 'dark'\nPlacet.isPreview; // true when in full-screen preview modal\nPlacet.fetch(url); // Server-side proxied HTTP (solves CORS)\nPlacet.respond(data); // Submit review response programmatically\nPlacet.toast(msg); // Show notification\nPlacet.resize(); // Auto-fit iframe height\n```\n\n#### Built-in Plugins\n\n| Plugin            | Description                                                     | Uses                              |\n| ----------------- | --------------------------------------------------------------- | --------------------------------- |\n| **form-submit**   | Renders a form from structured data, submits to a webhook       | `Placet.fetch`, env variables     |\n| **kroki-diagram** | Renders diagrams (Mermaid, PlantUML, D2, etc.) via Kroki server | `Placet.fetch`, server-side proxy |\n\nFor the full plugin development guide, see **[Plugin Documentation](docs/plugins.md)**.\n\n### Security\n\n- Sandboxed plugin iframes (`allow-scripts` only — no DOM, cookie, or storage access)\n- API keys: `hp_` prefixed, SHA256-hashed, never stored in plain text\n- JWT authentication with HttpOnly secure cookies\n- SSRF protection on outbound webhooks\n- Rate limiting on all endpoints\n- Server-side HTTP proxy for plugins (no direct browser requests)\n\n---\n\n## Production Deployment with Traefik\n\nPlacet ships with an optional [Traefik](https://traefik.io/) reverse proxy overlay for production deployments with automatic HTTPS.\n\n**1. Configure your domain:**\n\nAdd these to your `.env`:\n\n```bash\nCOMPOSE_FILE=docker-compose.yml:docker-compose.traefik.yml\nDOMAIN=placet.example.com\nACME_EMAIL=admin@example.com\n\n# Update these to match your domain\nNEXT_PUBLIC_WS_URL=https://placet.example.com\nNEXT_PUBLIC_APP_URL=https://placet.example.com\nCORS_ORIGIN=https://placet.example.com\n```\n\n**2. Start with Traefik:**\n\n```bash\nmake start\n# or directly:\ndocker compose up -d --build\n```\n\nWith `COMPOSE_FILE` set, Docker Compose automatically merges both files. Traefik will:\n\n- Obtain a Let's Encrypt TLS certificate for your domain\n- Route `https://your-domain.com` → Frontend\n- Route `https://your-domain.com/api/*` → Backend API\n- Route `https://your-domain.com/socket.io/*` → WebSocket\n- Redirect HTTP → HTTPS\n\n\u003e **Firewall:** The base compose file still exposes direct ports (3000, 3001, 9000, 9001). Use a firewall (ufw, iptables, cloud security group) to block external access — only ports 80 and 443 should be publicly reachable.\n\n\u003e **DNS:** Point your domain's A record to your server's IP before starting.\n\n---\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│                         Frontend                             │\n│              Next.js 16 · TailwindCSS v4 · shadcn/ui         │\n│                  Socket.io · TanStack Query                  │\n└──────────────────────┬───────────────────────────────────────┘\n                       │ /api/* proxy\n┌──────────────────────▼───────────────────────────────────────┐\n│                         Backend                              │\n│             NestJS · Fastify · Prisma · Socket.io            │\n├──────────────┬──────────────────┬────────────────────────────┤\n│  PostgreSQL  │      MinIO       │     Plugin Directory       │\n│   (data)     │   (file storage) │  (packages/plugins/*)      │\n└──────────────┴──────────────────┴────────────────────────────┘\n```\n\n### Tech Stack\n\n| Layer          | Technology                                                               |\n| -------------- | ------------------------------------------------------------------------ |\n| **Frontend**   | Next.js 16, React 19, TailwindCSS v4, shadcn/ui, TanStack Query, Zustand |\n| **Backend**    | NestJS 11, Fastify, Prisma 7, Socket.io                                  |\n| **Database**   | PostgreSQL 16                                                            |\n| **Storage**    | MinIO (S3-compatible)                                                    |\n| **Validation** | Zod (shared schemas between frontend \u0026 backend)                          |\n| **Monorepo**   | Turborepo + npm workspaces                                               |\n| **Container**  | Docker + Docker Compose                                                  |\n\n---\n\n## Project Structure\n\n```\nplacet/\n├── apps/\n│   ├── backend/          ← NestJS + Fastify + Prisma\n│   └── frontend/         ← Next.js 16 + TailwindCSS v4 + shadcn/ui\n├── examples/             ← Integration examples (Python, TypeScript, WebSocket, LangChain)\n├── packages/\n│   ├── shared/           ← Zod schemas + TypeScript types\n│   └── plugins/          ← Plugin directory (auto-discovered)\n│       ├── form-submit/\n│       └── kroki-diagram/\n├── docs/\n│   ├── plugins.md        ← Plugin development guide\n│   └── plugin-architecture.md\n├── docker-compose.yml\n├── Makefile\n├── .env.example\n└── turbo.json\n```\n\n---\n\n## Make Commands\n\n| Command                            | Description                                           |\n| ---------------------------------- | ----------------------------------------------------- |\n| `make setup`                       | First-time setup (install, build, Docker up, migrate) |\n| `make start`                       | Start all services                                    |\n| `make stop`                        | Stop all services                                     |\n| `make update`                      | Pull latest, rebuild, migrate                         |\n| `make test`                        | Run unit + e2e tests                                  |\n| `make lint`                        | Run linter across all packages                        |\n| `make validate`                    | Lint + format check + build                           |\n| `make validate-plugin PLUGIN=name` | Validate a plugin manifest and structure              |\n| `make logs`                        | Tail backend logs                                     |\n| `make clean`                       | Remove containers, volumes, node_modules              |\n| `make reset`                       | Full reset (clean + setup)                            |\n\n---\n\n## Configuration\n\nAll configuration lives in `.env` at the project root. Created automatically on first `make setup` from [`.env.example`](.env.example).\n\n| Variable                | Default              | Description                                                                  |\n| ----------------------- | -------------------- | ---------------------------------------------------------------------------- |\n| `LOG_FORMAT`            | `pretty`             | Log output format: `pretty` (colored, human-readable) or `json` (structured) |\n| `LOG_LEVEL`             | `info`               | Log level: `debug`, `info`, `warn`, `error`                                  |\n| `JWT_SECRET`            | (none)               | **Required.** Secret for JWT signing                                         |\n| `INITIAL_USER_EMAIL`    | `admin@placet.local` | Email for the auto-created admin user                                        |\n| `INITIAL_USER_PASSWORD` | `changeme`           | Password for the auto-created admin user                                     |\n| `CORS_ORIGIN`           | `*`                  | Allowed origins (comma-separated for production)                             |\n\nSee [`.env.example`](.env.example) for all available variables including database, MinIO, and push notification settings.\n\n---\n\n## Documentation\n\nThe full documentation is available at **[docs.placet.io](https://docs.placet.io)**.\n\n- [Quickstart](https://docs.placet.io/quickstart)\n- [Agent API Reference](https://docs.placet.io/api-reference)\n- [Integration Examples](https://docs.placet.io/integrations/overview) — Python, TypeScript, WebSocket, LangChain\n- [Plugin Development](https://docs.placet.io/plugins/overview)\n\nInteractive Swagger docs are also available locally at **`/api/docs`** when the backend is running.\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for how to submit PRs.\nSee [DEVELOPMENT.md](DEVELOPMENT.md) for local setup, conventions, and workflows.\n\n## License\n\nPlacet is open source under the [GNU Affero General Public License v3.0 (AGPL-3.0)](LICENSE).\n\nYou are free to use, modify, and distribute Placet under the terms of the AGPL-3.0. If you modify Placet and make it available over a network (e.g., as a SaaS), you must release your modifications under the same license.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for contributor terms.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplacet-io%2Fplacet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplacet-io%2Fplacet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplacet-io%2Fplacet/lists"}