Skip to content

Opinionated Stack

This file is the source of truth for the libraries Angee binds and what each one owns. The stack is opinionated so product and addon work starts from settled choices instead of re-litigating infrastructure. If a concern is listed here, use the library's native shape and keep Angee as thin glue.

Dependency changes must update this file in the same change.

How The Stack Is Locked

  • docs/stack.md owns concern boundaries: which library owns which job, and what thin glue Angee adds.
  • The core wheel's pyproject.toml owns its Python dependencies. Each folder addon's addon.toml owns that addon's dependencies; angee build projects the composed manifests into the host's generated [dependency-groups].addons key. uv.lock pins the resolved Python graph. The angee.graphql folder addon's manifest owns its Strawberry stack, Pydantic, Channels Redis adapter, and exact Strawberry fork reference. Use uv add / uv lock; do not use pip install by hand.
  • package.json owns JavaScript package scripts and declared dependencies. pnpm-workspace.yaml owns workspace membership. pnpm-lock.yaml pins the resolved JavaScript graph. Use pnpm add / pnpm install; do not use npm or yarn.
  • A dependency change is complete only when the concern row here and the owning pyproject.toml or addon manifest plus lockfile agree.

Backend

PickOwnsAngee adds
Python >= 3.14Runtime and typingProject conventions
Django 6.0+ORM, migrations, admin, auth contract, app registryAbstract bases and build-time composition into runtime apps
strawberry-djangoGraphQL types, resolvers, dataloaders, schema printingMerge addon schema parts into named schemas, changes subscription shortcuts, emit SDL, serve per name
django-choices-fieldEnum-backed model fieldsStateField semantic wrapper
strawberry-django-aggregates >= 0.10Aggregation and group-by resolvers, including exact grouped cardinalityAddon-level AggregateBuilder wiring (per addon, e.g. notes)
strawberry-django-hasura >= 0.7Expose Django models in the Hasura GraphQL dialect (_bool_exp/_aggregate/x_by_pk/_set, exact Decimal filters, nested to-one filter paths, nested to-many NestedInsert), exact grouped-count roots, plus computed (non-model) sources via a run_query RowSourceComposes it as the model emitter (hasura_model_resource, incl. lines= editable-child nested inserts) and the pydantic computed-source emitter (hasura_pydantic_resource)
pydanticTyped model validation/parsingRow-shape SSOT for computed (non-model) Hasura resources — the node + filter scalars derive from the pydantic model (hasura_pydantic_resource)
pydantic-ai-slim[anthropic,openai,mcp]In-process agent loop, tool calling, replayable message history, and MCP client; pydantic-graph is a transitive dependency but is not used by Angee directlyagents_runtime_pydantic adapts Angee inference backends and selected MCP rows into one bounded session turn; workflow durability and approval decisions remain owned by workflows / workflows_agents
Celery + RedisTask transport, worker execution, retries, queue routing, and periodic dispatchhosted by the angee.jobs framework app; the host/stack supplies broker topology; task bodies acquire Angee locks and delegate state changes to model/manager owners
croniterCron-expression schedule parsingschedule triggers compute next_fire_at (workflows)
python-dateutilRFC-5545 recurrence-rule parsing and expansion (rrulestr)angee.scheduling owns recurrence — RecurrenceField (a validated RRULE column) + Recurrence.occurrences(window), bounded, timezone-aware expansion in the project TIME_ZONE
phonenumbersRegion-aware telephone parsing, validation, matching, and E.164 formattingparties.Handle.normalize_value parses phone/WhatsApp values with region=None, so canonical E.164 input requires a leading +country code; invalid, impossible, or region-unknown values use the digit-only comparison fallback, and signature evidence mines through the same owner
channels + channels-redis + uvicornASGI/WebSocket transport and serving; Redis-backed channel layer for production fanoutGraphQL subscription mounting; uvicorn serves the composed ASGI app and sends the lifespan that enters the MCP mount's http_app lifespan (angee.asgi); in-memory channel layer remains dev/test only
django-zed-rebacREBAC engine, actor scoping, relationship storage, local and SpiceDB-compatible backendsPer-addon schema merge, reserved roles, actor resolver
django-axesLogin failure throttling at Django's authenticate()/auth-backend signal seamIAM composes the app, standalone backend, and middleware so password GraphQL login stays a thin authenticate(request=...) caller
django-sqidsOpaque external IDsSqidMixin, SqidField (NULL-safe decode on joins), GraphQL boundary scalar
django-simple-historyShadow history tables and revertHistoryMixin marker (knowledge Vault/Page; messaging edits are in-row edit_history + immutable fragments instead)
django.contrib.postgresPostgres full-text search (SearchVectorField, GinIndex, SearchQuery)messaging.Fragment.search — stamped once at fragment creation (content-addressed rows are immutable, so no trigger/queue); dedup indexes each unique text exactly once. Postgres-only: the SQLite test backend leaves the vector NULL
django-reversionVersioned field snapshots and revertRevisionMixin convenience API, composer-emitted model registration
cryptographyEncryption primitivesEncryptedField (Fernet at rest, secret-by-type)
django-import-export + tablibResource import/export resources, tabular formats, row cleaning, and row resultsTiered manifests, xref ledger, and frozen-tier policy
pyyamlYAML parsing substrateResource loader reads .yaml/.yml resource files; django-yamlconf consumes project settings YAML
ruamel.yamlComment/format-preserving round-trip YAML editingThe AddonInstaller's settings.yaml INSTALLED_APPS install/uninstall edit — the one writer that must preserve operator comments and layout (pyyaml round-trips lose them); not used at boot
django-yamlconfDjango settings YAML overlaysangee.compose.settings loads settings.yaml beside manage.py; Composer applies addon autoconfig.py fragments
django-environTyped boot environment access and URL parsersangee.compose.settings reads Angee bootstrap env vars and honors the standard service URLs a deployment injects — DATABASE_URLDATABASES, CACHE_URLCACHES, EMAIL_URLEMAIL_* — override-safe (an explicit project setting wins), falling back to the SQLite/Django floor when unset
django-anymailVendor-neutral Django email backend API across transactional ESPs, plus the deterministic test backendangee.messaging renders outbound Message parts and envelopes into AnymailMessage; deployments select an ESP through EMAIL_BACKEND plus ANYMAIL/ANYMAIL_* environment settings, while an unconfigured stack logs and declines delivery without touching Django's implicit localhost SMTP backend
pyjwt[crypto]JWT/JOSE signature + claims verification and JWKS fetchOIDC id_token verification (OAuthClientOidcProtocol.verify_id_token); kept because authlib.jose is deprecated. The OAuth2 token exchange itself is owned by authlib
authlibOAuth2/OIDC client protocol — authorization-code + refresh-token requests, client authentication, PKCE (RFC 7636), and token revocation (RFC 7009)Thin per-OAuthClient OAuth2Client adapter behind the stable OAuthClientProtocol seam, plus the non-standard JSON-token-body shim; id_token verification stays on pyjwt
httpxHTTP client/transport for all integrate outbound callsintegrate.http.PinnedTransport — an SSRF-pinned httpx transport that resolves once and dials a validated IP (judgement owned by integrate.net.is_unsafe_address) with system-store TLS. Composed by HttpClient (the integration backends) and by the OAuth client (handed to authlib's OAuth2Client); the honest Angee-Integrate/1.0 UA and an injected transport test seam ride on it
httpcorehttpx's low-level connection pool + network backendintegrate.http._PinnedBackend subclasses httpcore.SyncBackend and overrides connect_tcp to dial the validated IP; PinnedTransport swaps it into the pool's _network_backend. Named and bounded directly because the SSRF pin owns httpcore's SyncBackend/ConnectionPool API, not just via httpx
mcp (jlowin FastMCP v2)MCP server — tool registration, JSON-RPC, StreamableHTTP ASGI app, bearer auth (TokenVerifier), per-call middlewareMounts one StreamableHTTP app at /mcp via the asgi.py http_mounts seam (its http_app lifespan entered by angee.asgi via router.lifespan_context), authenticates the bearer to a REBAC actor with a fastmcp.server.auth.TokenVerifier and brackets each tool call in that actor; addon tools — incl. GraphQLTool operations executed under the actor (angee.mcp.graphql) — run scoped, and rebac authorizes
anthropicAnthropic Claude API SDK — Messages API client, model catalogue, retries, typed SDK modelsagents_integrate_anthropic maps Angee inference providers/models to the SDK and contributes the backend into ANGEE_INFERENCE_BACKEND_CLASSES
openaiOpenAI Python SDK — Chat Completions client for OpenAI and compatible endpoints, model catalogue, retries, typed SDK modelsagents_integrate_openai maps Angee inference providers/models to the SDK and contributes the reusable backend into ANGEE_INFERENCE_BACKEND_CLASSES; compatible addons such as Ollama specialize it without another dependency
python-magicMIME detection from file bytesStorage finalize detection (requires the system libmagic)
vobjectvCard/iCalendar parse + serialiseparties_integrate_carddav parses CardDAV vCards into parties/handles/addresses (and serialises for round-trip)
IMAPClientIMAP4rev1 protocol client — TLS/STARTTLS, LOGIN/XOAUTH2 auth, modified-UTF-7 folder names, SPECIAL-USE flags, typed UID SEARCH/FETCH responsesmessaging_integrate_imap drives incremental mailbox sync over it (per-mailbox UIDVALIDITY/UIDNEXT cursors on the channel bridge); MIME parsing stays on the stdlib email package
slack-sdkOfficial Slack Web API client — user-token authentication, cursor pagination, typed API failures, and server-declared rate limitsmessaging_integrate_slack serially polls one channel per workspace install and maps conversations onto chat threads in bounded, resumable pages; recently active thread parents carry independent reply watermarks so replies newer than the conversation history watermark still land. The supported deployment is a bring-your-own internal Slack app: internal apps retain the high-volume conversations.history/conversations.replies limits needed for backfill and polling, while distributed non-Marketplace apps are subject to the 2025 one-request-per-minute/15-object throttle and are not viable for this bridge. A future Socket Mode live layer must remain an optional phase-2 seam after user-scoped event delivery is verified; polling is the correctness foundation.
mail-parser-replyEmail body segmentation — splits a plain-text body into replies at multi-language attribution headers ("On …, X wrote:", Outlook From:-blocks, "-----Original Message-----") and detects signatures (the RFC 3676 -- delimiter and salutation tails like "Best regards,") and trailing disclaimersmessaging_integrate_imap's split_plain_text drives it (languages=["en","fr","de","es","it"]) to role-tag body/quoted/signature parts; quote-marker stripping stays local so a quoted paragraph content-addresses to the original body's Fragment rows
neonizeWhatsApp Web multi-device protocol — whatsmeow (Go) via a bundled platform library: QR-code device pairing, the encrypted session store (SQLite), connect/message/receipt event callbacks, media download + decryptmessaging_integrate_whatsapp runs one live session per channel inside the dedicated whatsapp Celery queue worker and maps events through its parser onto Message.objects.ingest; its backend key contributes the whatsapp segment to the shared angee.integrate.live.session_store_path convention (ANGEE_DATA_DIR/<backend key>/<sqid>)
qrcode[pil]QR code generation — segment/error-correction encoding, PNG rendering via PillowWorker-only angee.integrate.session._qr_data_uri renders pairing payloads to PNG data-URIs carried in bridge sync_progress; the [pil] extra declares the Pillow renderer directly rather than leaning on a vendor SDK's transitive Pillow
telethonTelegram MTProto client protocol — user-account auth (QR + 2FA), session persistence, update/event callbacks, media downloadmessaging_integrate_telegram runs one live session per channel on the dedicated telegram queue; angee.integrate.live.session_store_path owns its store convention at ANGEE_DATA_DIR/<backend key>/<sqid>, and events map through the addon's identity rules onto Message.objects.ingest. Install from current PyPI with >=1.44: the GitHub repository was archived in 2026-02 and development moved to Codeberg; do not chase unreleased v2 or substitute the dead/stalled/unclear Pyrogram, hydrogram, or dlgram lineages
discord.pyDiscord Gateway bot client — bot-token login, Gateway events/reconnects, REST history, and rate-limit handlingmessaging_integrate_discord runs one sanctioned bot connection per channel on the dedicated discord queue and maps every invited guild channel (plus DMs sent to the bot) into threads. Enable the privileged MESSAGE_CONTENT intent in the Developer Portal. A bot cannot read the user's personal DMs or uninvited guilds; self-bot/user-token access violates Discord's Terms of Service and is explicitly out of scope.
signal-cli >= 0.14.6 (system binary)Signal linked-device protocol, encrypted account store, JSON-RPC receive stream, and attachment downloadmessaging_integrate_signal runs one native signal-cli child per channel on the dedicated signal queue and keeps its config under ANGEE_DATA_DIR/signal/<sqid>. Install the native Homebrew build on supported macOS hosts or the upstream -Linux-native release on Linux rather than adding a Python/JRE dependency. Pin the exact binary release in deployment, monitor upstream releases, and update inside Signal's roughly three-month client capability clock: an obsolete linked device can be remotely unlinked, and no version pin is durable indefinitely. SIGNAL_CLI_BIN may select the pinned executable path.
mautrix[encryption] + python-olmMatrix client protocol and E2EE — password login, sync, media negotiation, encrypted attachments/rooms, SSSS recovery keys, and cross-signingmessaging_integrate_matrix puppets one user's own Matrix account on the dedicated matrix queue and keeps mautrix's state/crypto stores under ANGEE_DATA_DIR/matrix/<sqid>. MPL-2.0. matrix-nio was rejected as stale and without current cross-signing support. mautrix still uses libolm through python-olm; the locked [tool.uv.extra-build-variables] CXXFLAGS=-fdelayed-template-parsing setting builds it on the supported Python 3.14/macOS toolchain. E2EE is mandatory and imports fail fast when its boundary is incomplete.
asyncpg + aiosqlitemautrix persistent crypto-store database driversPgCryptoStore imports asyncpg for its shared store implementation, while the Matrix addon opens that store through mautrix's sqlite:// database adapter backed by aiosqlite. Both are direct runtime dependencies so encrypted-room support cannot silently degrade.
cryptgNative AES-IGE for MTProto — the encryption hot pathDeclared directly rather than via Telethon's optional extra, so the maintained native implementation is locked instead of falling back to pure-Python pyaes on a long-lived ingester
markdown-it-pyCommonMark tokenizer with source line spans (block token .map)knowledge slices doc sections by heading without re-rendering — the MarkdownPage structure methods (parse_outline/outline, section_range, spliced_section, spliced_unique) shared by the outline read field and the section-anchored patch write
uvPython dependency resolution and workspacesWorkspace layout

Money representation: money stays native — a DecimalField (default max_digits=18, decimal_places=6) paired with MoneyField's currency_field path declaration, never a money library. angee.money owns the currency catalogue, dated exchange rates, and conversion; the reference currency is a required project setting.

State-transition library choice: angee.base.transitions is deliberately owned in-repo. django-fsm-2 and viewflow.fsm were evaluated and rejected because Angee's guard is REBAC permission-as-query with a settings-backed policy overlay and composer revalidation, which a user-callable permission hook cannot express.

Tree library choice: HierarchyMixin's materialized path is deliberately owned in-repo. django-tree-queries and treebeard were evaluated and rejected because the indexed path-prefix subtree test composes into the Hasura _bool_exp filter dialect and REBAC subtree scoping as a plain column predicate; recursive CTE ownership does not.

Implementation-registry choice: the settings-keyed ImplClassField registry is Angee's declared composition contract. Python entry points were evaluated and rejected because composition facts belong to project settings, not package metadata.

Audit-history exclusions: django-easy-audit was evaluated and rejected because GPL code is incompatible with a framework composed into commercial consumers. django-reversion-compare was evaluated and rejected for the same GPL reason; django-reversion itself remains the locked owner for snapshots and revert.

Frontend

PickOwnsAngee adds
React 19View libraryComponent conventions
TypeScript >= 6Language and type systemBranded boundary types
valibotRuntime parsing and narrowing for opaque GraphQL JSON-scalar fieldsDomain owners declare Valibot schemas and parse at the boundary instead of asserting an application shape
@refinedev/coreResource registry, standard data hooks, react-query cache/invalidation, auth/i18n/live provider contractsAngee projects emitted angee.resources metadata to refine resources and mounts one composed <Refine> root with named providers and the TanStack Router binding
@refinedev/hasura + graphql-request 5 + graphql 16Hasura GraphQL data provider (_bool_exp, order_by, _aggregate, _by_pk, _set) and authored meta.gqlQuery / meta.gqlMutation executionAngee pins idType: "String" and namingConvention: "hasura-default", uses refine-compatible GraphQL document ASTs, and applies session/CSRF or service auth at the transport boundary
graphql-ws 6 + 5GraphQL WebSocket lifecycle for the Hasura live provider and daemon-owned operator transportEndpoint derivation, connection params, retry policy, and the operator daemon subscription + raw log socket transport — request/response now rides a Refine operator data provider, leaving only the intrinsically streaming surfaces on this ws transport. Two majors resolve honestly: the Hasura live provider pulls graphql-ws@6 (the peer of @refinedev/hasura@7), while the operator daemon transport pins ^5.16.2
GraphQL Code Generator (client-preset) + @graphql-typed-document-node/core + graphql-tagGenerated TypeScript schema and operation types from emitted Django SDL and daemon-owned SDL, as TypedDocumentNode documents; graphql-tag parses the schema-independent core document@angee/app owns the one angee-web-codegen CLI: it reads runtime/web/manifest.json, generates each Django schema from runtime/schemas/<schema>.graphql (routing documents by filename: documents.ts/documents.console.ts → console, documents.public.ts → public), derives authored action/aggregate/group/delete-preview/revision documents, and emits the composed runtime/web/app.ts. The operator daemon joins the same pass as an external [web].codegen manifest entry — its committed SDL read straight from the operator package, scanning only documents.daemon.ts, with a bare typescript types module the console re-exports. Addon-authored operations carry no hand-written result/variables types; the framework's BaseImplChoices is hand-typed against its core-owned Python projection so the React packages remain independent of a composed schema fixture.
TanStack RouterType-safe routing and search paramsdefineAddon to createApp route composition and flat URL search codec
@refinedev/react-hook-form + react-hook-form + @hookform/resolvers + zodForm state, submit lifecycle, and validation bindingFormView keeps Angee's declarative rendered DSL while delegating state/validation to refine/react-hook-form
@refinedev/react-table + TanStack TableServer-backed table state, sort/filter/pagination bridge, columns, grouping, selectionListView and BoardView keep Angee's rendered controls and domain view modes while delegating standard table/data mechanics
TanStack VirtualRow and column virtualizationLong-list wiring
nuqsType-safe URL query stateRemaining chrome query state such as top-menu tabs
i18nextRuntime i18n@angee/app owns one instance; addons contribute namespace-relative bundles and @angee/ui exposes the namespace hook factory
date-fnsDate and relative-time formattingDate and timestamp widgets
use-debounceDebounced React values and callbacksSearch and filter inputs
Tailwind 4Token styling engineSemantic token set
tailwind-mergeSafe class mergingcn() helper
lucide-reactIconsName-referenced icon registry
ViteBundling, dev server, HMRProject integration
@agentclientprotocol/sdkACP client — agent JSON-RPC session, prompt/cancel, session-update stream (the agent image runs @agentclientprotocol/claude-agent-acp; both replace the deprecated @zed-industries/* names)WebSocket ndjson transport to a routed agent + assistant-ui runtime bridge
@assistant-ui/reactChat thread UI — message store, composer, tool-call renderingACP-streaming runtime adapter and styled thread surface
streamdownStreamed-markdown render for assistant chunksAssistant message body in the agent chat
react-pdf (+ pdfjs-dist)Inline PDF rendering (pdf.js)storage file previewer
@vidstack/reactInline video/audio playerstorage file previewer
heic-toClient-side HEIC/HEIF decode to a displayable image (current libheif-wasm)storage HEIC previewer
pnpmJavaScript dependency resolution and workspacesWorkspace layout
Node >= 22.13JavaScript build runtimeProject runtime

Chat-UI library choice: @assistant-ui/react owns the chat-UX surface (composed over ACP); CopilotKit and @headlessui/react were evaluated and rejected, and TanStack AI is a watch item.

Hasura Dialect Rule

strawberry-django-hasura, the operator daemon SDL, @refinedev/hasura, and Angee's refine/data glue share one Hasura-default wire contract. Because the daemon already speaks this contract, the operator web package consumes it as a Refine operator data provider (bearer-authed createAngeeHasuraDataProvider) for request/response, the same shape as the console/public providers; only its live subscriptions and the raw log socket stay on the daemon ws transport. Grouped resources must keep the DDN/NDC-preview shape: <resource>_groups(group_by, where, having, order_by, limit, offset): [<resource>_group!]!, with each group returning a typed key: <Model>GroupKey! and the free aggregate: <Model>Aggregate!. The stock <resource>_aggregate root remains the unmodified refine/Hasura aggregate surface; grouped roots are authored operations owned by the dialect adapters.

Future grouped features such as bucket ordering, bucket predicates, additional date extraction, or JSON drill-down operators must be added at the dialect owners together: the Django adapter, operator SDL, emitted resource metadata, and the refine authored-operation helpers. Do not add frontend-only group semantics or local provider dialects.

Rendered Binding

Angee's frontend is Refine-native: the app composes one <Refine> root, resource metadata projects into refine resources, and the rendered binding owns only domain presentation over refine state. The active frontend owners are @angee/app, @angee/refine, @angee/metadata, and @angee/ui.

PickOwnsAngee adds
@base-ui/reactHeadless primitives: dialog, popover, menu, tabs, tooltip, field, toolbar, scroll area, and related UIStyled binding and composition rules; controlled open/onOpenChange owns popover/dialog transition timing
@floating-ui/react-domFloating-element positioning and virtual anchorsPopover and menu anchoring
@angee/logo-reactAngee brand logo and cube marksBrand lockup in the public layout
react-markdown + remark-gfmMarkdown rendering (GitHub-flavored)Markdown widget preview
tailwind-variantsVariant recipes with slotsComponent recipes
tw-animate-cssTailwind 4 animation utilitiesMotion tokens
cmdkCommand menuSpotlight command surface
react-day-pickerCalendarDate widgets
react-resizable-panelsSplit panesLayout and inspector panes
CodeMirror 6 (+ @codemirror/lang-json)Text / Markdown / JSON editorMarkdown and JSON widget editors (shared useCodeMirrorEditor)
react-json-view-liteJSON value tree renderingJSON widget read tree and debug JSON panels
@xyflow/reactnode/edge graph canvas@angee/ui GraphView canvas
@dagrejs/dagredirected-graph layout@angee/ui GraphView node placement
FullCalendar (Standard: @fullcalendar/react + @fullcalendar/daygrid + @fullcalendar/timegrid + @fullcalendar/interaction)Month/week/day event calendar, drag/resize/select@angee/ui CalendarView renders server-expanded occurrences and wires interactions to auto-CRUD; code-split behind a lazy import and themed through the token set
@dnd-kitDrag and dropBoard and rail interactions
Native browser drag/dropFile drag enter/leave/drop events and DataTransfer.files@angee/ui upload drop target primitive

Event-calendar library choice: FullCalendar Standard (all four packages MIT) owns the month/week/day event surface. @schedule-x was evaluated and rejected because its drag-and-drop and drag-to-create plugins — exactly the two interactions a framework calendar primitive requires — are premium/paid, a license cliff on code every consumer inherits (revisit only if it relicenses). FullCalendar's commercial @fullcalendar/resource-* views stay out, a separate license decision at the W4/W5 resource calendars. Distinct from react-day-picker above, which owns the date-picker day grid behind date widgets, not an event calendar.

Tooling

PickOwnsAngee adds
Cobraangee CLI implementationDev supervisor, init, workspaces, templates
hatchlingPython wheel buildPackage metadata conventions
ruffPython lint and formatRepo checks
mypyPython type checkingStrict backend checks
pytest + pytest-djangoBackend testsSynthetic project and integration fixtures
FakerTest and seed data generationBulk lorem fixtures (e.g. seed_lorem_notes)
VitestTypeScript and React testsFrontend unit checks
happy-domDOM environment for VitestPer-file env opt-in for hook and component tests
@testing-library/reactReact component and hook test renderingProvider-wrapped render and hook harnesses
PlaywrightBrowser tests@angee/e2e harness: workspace-isolated runner, role storageState login, GraphQL api fixture, Page Object base (E2E guide)
@playwright/mcpInteractive browser-driving for host coding agentsRepo-root .mcp.json server (npx-run, pinned), bound to the base stack's chrome-profile (.angee/data/chrome); the agent navigates to the stack's ANGEE_UI_PORT (:5173). Distinct from @angee/e2e (the deterministic test runner) and agents.MCPServer (the MCP config rendered for operator-provisioned product agents)
StorybookComponent workshop@angee/ui and addon previews
GitHub ActionsCIBuild, lint, type, test gates
CopierProject and addon templatesAngee templates

Proposed, Not Locked

PickRole
Yjs + HocuspocusCollaborative editing
pgvector / sqlite-vec / python-igraph / lightrag-hkuVector search and graph RAG
django-ninjaTyped REST sidecars (callbacks, webhooks, health) — over the locked pydantic
boto3S3-compatible storage backend (S3 / R2 / MinIO presigned IO)
ansi-to-reactANSI log panels
simple-icons + @lobehub/iconsBrand and vendor SVG icon registry

Change Policy

  • Add a dependency only with an owner row here.
  • Remove a dependency by deleting its row.
  • Swap a dependency by updating the row and explaining why in the change.
  • Move proposed picks into a locked section before shipping code that depends on them.

Released under the AGPL-3.0 License.