{"owner":"langflow-ai","repo":"langflow","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI coding agents when working with code in this repository.\n\n## Project Overview\n\nLangflow is a visual workflow builder for AI-powered agents. It has a Python/FastAPI backend, React/TypeScript frontend, and a lightweight executor CLI (lfx).\n\n## Prerequisites\n\n- **Python:** 3.10-3.14\n- **uv:** >=0.4 (Python package manager)\n- **Node.js:** >=20.19.0 (v22.12 LTS recommended)\n- **npm:** v10.9+\n- **make:** For build coordination\n\n## Common Commands\n\n### Development Setup\n```bash\nmake init              # Install all dependencies + pre-commit hooks\nmake run_cli           # Build and run Langflow (http://localhost:7860)\nmake run_clic          # Clean build and run (use when frontend issues occur)\n```\n\n### Development Mode (Hot Reload)\n```bash\nmake backend           # FastAPI on port 7860 (terminal 1)\nmake frontend          # Vite dev server on port 3000 (terminal 2)\n```\n\nFor component development, enable dynamic loading:\n```bash\nLFX_DEV=1 make backend                    # Load all components dynamically\nLFX_DEV=mistral,openai make backend       # Load only specific modules\n```\n\n### Code Quality\n```bash\nmake format_backend    # Format Python (ruff) - run FIRST before lint\nmake format_frontend   # Format TypeScript (biome)\nmake format            # Both\nmake lint              # mypy type checking\n```\n\n### Testing\n```bash\nmake unit_tests                    # Backend unit tests (pytest, parallel)\nmake unit_tests async=false        # Sequential tests\nuv run pytest path/to/test.py      # Single test file\nuv run pytest path/to/test.py::test_name  # Single test\n\nmake test_frontend                 # Jest unit tests\nmake tests_frontend                # Playwright e2e tests\n```\n\n### Database Migrations\n```bash\nmake alembic-revision message=\"Description\"  # Create migration\nmake alembic-upgrade                         # Apply migrations\nmake alembic-downgrade                       # Rollback one version\n```\n\n## Architecture\n\n### Monorepo Structure\n```\nsrc/\n├── backend/\n│   ├── base/langflow/     # Core backend package (langflow-base)\n│   │   ├── api/           # FastAPI routes (v1/, v2/)\n│   │   ├── components/    # Built-in Langflow components\n│   │   ├── services/      # Service layer (auth, database, cache, etc.)\n│   │   ├── graph/         # Flow graph execution engine\n│   │   └── custom/        # Custom component framework\n│   └── tests/             # Backend tests\n├── frontend/              # React/TypeScript UI\n│   └── src/\n│       ├── components/    # UI components\n│       ├── stores/        # Zustand state management\n│       └── icons/         # Component icons\n└── lfx/                   # Lightweight executor CLI\n```\n\n### Key Packages\n- **langflow**: Main package with all integrations\n- **langflow-base**: Core framework (api, services, graph engine)\n- **lfx**: Standalone CLI for running flows (`lfx serve`, `lfx run`)\n\n### Service Layer\nBackend services in `src/backend/base/langflow/services/`:\n- `auth/` - Authentication\n- `authorization/` - Authorization (RBAC) plugin layer — see below\n- `database/` - SQLAlchemy models and migrations\n- `cache/` - Caching layer\n- `storage/` - File storage\n- `tracing/` - Observability integrations\n\n### Authorization (RBAC)\n\nAuthorization is a pluggable layer separate from authentication:\n\n- **OSS** ships the interface (`BaseAuthorizationService` in `lfx`) + a pass-through implementation (`LangflowAuthorizationService`) + the `authz_*` and `casbin_rule` DB schema + route guards.\n- Implementations register via the `lfx.services` entry point `authorization_service` in `lfx.toml` (same pattern as the SSO `auth_service`). A registered plugin reads the `authz_*` admin tables and writes compiled rules to `casbin_rule`.\n\nDefault is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.\n\nRoute guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):\n- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute\n- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`\n- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`\n- `ensure_knowledge_base_permission(user, KnowledgeBaseAction.*, kb_name=..., kb_user_id=...)`\n- `ensure_variable_permission(user, VariableAction.*, variable_id=..., variable_user_id=...)`\n- `ensure_file_permission(user, FileAction.*, file_id=..., file_user_id=...)`\n- `ensure_share_permission(user, ShareAction.*, share_id=..., share_user_id=...)`\n- `filter_visible_resources(user, resource_type=..., candidates=..., act=...)` — list-endpoint filter; safe no-op in OSS\n\nThe enforcement request shape is `(subject, domain, object, action)`:\n- subject = `user:{uuid}`\n- domain = `project:{uuid}` → `workspace:{uuid}` → `*` (resolved by `_resolve_flow_domain`; the more specific domain wins so project-scoped grants match directly while workspace-scoped grants still flow down via plugin-side role inheritance)\n- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.\n- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`\n\n**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.\n\n**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.\n\n**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.\n\n**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `\"{resource}:{action}\"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.\n\n## Component Development\n\nComponents live in `src/backend/base/langflow/components/`. To add a new component:\n\n1. Create component class inheriting from `Component`\n2. Define `display_name`, `description`, `icon`, `inputs`, `outputs`\n3. Add to `__init__.py` (alphabetical order)\n4. Run with `LFX_DEV=1 make backend` for hot reload\n\n**IMPORTANT:** Changing a component's class name is a breaking change and should never be done. The class name serves as an identifier used to match components in saved flows and to flag them for updates in the UI. Renaming it will break existing flows that use that component.\n\n### Component Structure\n```python\nfrom langflow.custom import Component\nfrom langflow.io import MessageTextInput, Output\n\nclass MyComponent(Component):\n    display_name = \"My Component\"\n    description = \"What it does\"\n    icon = \"component-icon\"  # Lucide icon name or custom\n\n    inputs = [\n        MessageTextInput(name=\"input_value\", display_name=\"Input\"),\n    ]\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"process\"),\n    ]\n\n    def process(self) -> Message:\n        # Component logic\n        return Message(text=self.input_value)\n```\n\n### Component Testing\nTests go in `src/backend/tests/unit/components/`. Use base classes:\n- `ComponentTestBaseWithClient` - Components needing API access\n- `ComponentTestBaseWithoutClient` - Pure logic components\n\nRequired fixtures: `component_class`, `default_kwargs`, `file_names_mapping`\n\n## Frontend Development\n\n- **React 19** + TypeScript + Vite\n- **Zustand** for state management\n- **@xyflow/react** for graph visualization\n- **Tailwind CSS** for styling\n\n### Custom Icons\n1. Create SVG component in `src/frontend/src/icons/YourIcon/`\n2. Export with `forwardRef` and `isDark` prop support\n3. Add to `lazyIconImports.ts`\n4. Set `icon = \"YourIcon\"` in Python component\n\n## Testing Notes\n\n- `@pytest.mark.api_key_required` - Tests requiring external API keys\n- `@pytest.mark.no_blockbuster` - Skip blockbuster plugin\n- Database tests may fail in batch but pass individually\n- Pre-commit hooks require `uv run git commit`\n- Always use `uv run` when running Python commands\n- When running tests inside a sub-package (e.g. `langflow-base`, `lfx`), sync that package's dev group first: `uv sync --group dev --package langflow-base`. The default `uv sync` only resolves the top-level workspace and may leave dev-only test deps (e.g. `fakeredis`) uninstalled.\n\n### Graph Testing Pattern\n\nProper Graph tests follow this pattern:\n1. Build graph with connected components\n2. Connect them via `.set()` calls\n3. Call `async_start` and iterate over the results\n4. Validate the results\n\n### Testing Best Practices\n\n- Avoid mocking in tests when possible\n- Prefer real integrations for more reliable tests\n\n## Version Management\n```bash\nmake patch v=1.5.0  # Update version across all packages\n```\n\nThis updates: `pyproject.toml`, `src/backend/base/pyproject.toml`, `src/frontend/package.json`\n\n## Pre-commit Workflow\n\nPre-commit hooks run ruff and biome automatically on `git commit`, so manual\nformatting is not required. To avoid an extra commit cycle when you have many\nchanges:\n\n1. Run `make format_backend` once before staging - fixes most ruff issues up front.\n2. Run `uv run git commit` (the `uv run` ensures pre-commit finds the right Python).\n3. If you touched backend code, run `make unit_tests` locally for faster feedback than CI.\n\n## Pull Request Guidelines\n\n- Follow [semantic commit conventions](https://www.conventionalcommits.org/)\n- Reference any issues fixed (e.g., `Fixes #1234`)\n- Ensure all tests pass before submitting\n\n## Documentation\n\nDocumentation uses Docusaurus and lives in `docs/`:\n```bash\ncd docs\nyarn install\nyarn start        # Dev server on port 3000 (prompts for 3001 if 3000 is in use)\n```\n","CLAUDE.md":"# CLAUDE.md\n\n@AGENTS.md\n@.claude/CLAUDE.md\n\nThis project uses [AGENTS.md](https://agents.md/) as the standard for providing context to AI coding agents. The `@AGENTS.md` import above tells Claude Code to load `AGENTS.md` automatically; other tools that natively support `AGENTS.md` will pick it up directly. The `@.claude/CLAUDE.md` import loads the local hard-rules file (gitignored) that mirrors the PostToolUse hook policy.\n"},"files":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI coding agents when working with code in this repository.\n\n## Project Overview\n\nLangflow is a visual workflow builder for AI-powered agents. It has a Python/FastAPI backend, React/TypeScript frontend, and a lightweight executor CLI (lfx).\n\n## Prerequisites\n\n- **Python:** 3.10-3.14\n- **uv:** >=0.4 (Python package manager)\n- **Node.js:** >=20.19.0 (v22.12 LTS recommended)\n- **npm:** v10.9+\n- **make:** For build coordination\n\n## Common Commands\n\n### Development Setup\n```bash\nmake init              # Install all dependencies + pre-commit hooks\nmake run_cli           # Build and run Langflow (http://localhost:7860)\nmake run_clic          # Clean build and run (use when frontend issues occur)\n```\n\n### Development Mode (Hot Reload)\n```bash\nmake backend           # FastAPI on port 7860 (terminal 1)\nmake frontend          # Vite dev server on port 3000 (terminal 2)\n```\n\nFor component development, enable dynamic loading:\n```bash\nLFX_DEV=1 make backend                    # Load all components dynamically\nLFX_DEV=mistral,openai make backend       # Load only specific modules\n```\n\n### Code Quality\n```bash\nmake format_backend    # Format Python (ruff) - run FIRST before lint\nmake format_frontend   # Format TypeScript (biome)\nmake format            # Both\nmake lint              # mypy type checking\n```\n\n### Testing\n```bash\nmake unit_tests                    # Backend unit tests (pytest, parallel)\nmake unit_tests async=false        # Sequential tests\nuv run pytest path/to/test.py      # Single test file\nuv run pytest path/to/test.py::test_name  # Single test\n\nmake test_frontend                 # Jest unit tests\nmake tests_frontend                # Playwright e2e tests\n```\n\n### Database Migrations\n```bash\nmake alembic-revision message=\"Description\"  # Create migration\nmake alembic-upgrade                         # Apply migrations\nmake alembic-downgrade                       # Rollback one version\n```\n\n## Architecture\n\n### Monorepo Structure\n```\nsrc/\n├── backend/\n│   ├── base/langflow/     # Core backend package (langflow-base)\n│   │   ├── api/           # FastAPI routes (v1/, v2/)\n│   │   ├── components/    # Built-in Langflow components\n│   │   ├── services/      # Service layer (auth, database, cache, etc.)\n│   │   ├── graph/         # Flow graph execution engine\n│   │   └── custom/        # Custom component framework\n│   └── tests/             # Backend tests\n├── frontend/              # React/TypeScript UI\n│   └── src/\n│       ├── components/    # UI components\n│       ├── stores/        # Zustand state management\n│       └── icons/         # Component icons\n└── lfx/                   # Lightweight executor CLI\n```\n\n### Key Packages\n- **langflow**: Main package with all integrations\n- **langflow-base**: Core framework (api, services, graph engine)\n- **lfx**: Standalone CLI for running flows (`lfx serve`, `lfx run`)\n\n### Service Layer\nBackend services in `src/backend/base/langflow/services/`:\n- `auth/` - Authentication\n- `authorization/` - Authorization (RBAC) plugin layer — see below\n- `database/` - SQLAlchemy models and migrations\n- `cache/` - Caching layer\n- `storage/` - File storage\n- `tracing/` - Observability integrations\n\n### Authorization (RBAC)\n\nAuthorization is a pluggable layer separate from authentication:\n\n- **OSS** ships the interface (`BaseAuthorizationService` in `lfx`) + a pass-through implementation (`LangflowAuthorizationService`) + the `authz_*` and `casbin_rule` DB schema + route guards.\n- Implementations register via the `lfx.services` entry point `authorization_service` in `lfx.toml` (same pattern as the SSO `auth_service`). A registered plugin reads the `authz_*` admin tables and writes compiled rules to `casbin_rule`.\n\nDefault is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.\n\nRoute guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):\n- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute\n- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`\n- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`\n- `ensure_knowledge_base_permission(user, KnowledgeBaseAction.*, kb_name=..., kb_user_id=...)`\n- `ensure_variable_permission(user, VariableAction.*, variable_id=..., variable_user_id=...)`\n- `ensure_file_permission(user, FileAction.*, file_id=..., file_user_id=...)`\n- `ensure_share_permission(user, ShareAction.*, share_id=..., share_user_id=...)`\n- `filter_visible_resources(user, resource_type=..., candidates=..., act=...)` — list-endpoint filter; safe no-op in OSS\n\nThe enforcement request shape is `(subject, domain, object, action)`:\n- subject = `user:{uuid}`\n- domain = `project:{uuid}` → `workspace:{uuid}` → `*` (resolved by `_resolve_flow_domain`; the more specific domain wins so project-scoped grants match directly while workspace-scoped grants still flow down via plugin-side role inheritance)\n- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.\n- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`\n\n**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.\n\n**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.\n\n**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.\n\n**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `\"{resource}:{action}\"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.\n\n## Component Development\n\nComponents live in `src/backend/base/langflow/components/`. To add a new component:\n\n1. Create component class inheriting from `Component`\n2. Define `display_name`, `description`, `icon`, `inputs`, `outputs`\n3. Add to `__init__.py` (alphabetical order)\n4. Run with `LFX_DEV=1 make backend` for hot reload\n\n**IMPORTANT:** Changing a component's class name is a breaking change and should never be done. The class name serves as an identifier used to match components in saved flows and to flag them for updates in the UI. Renaming it will break existing flows that use that component.\n\n### Component Structure\n```python\nfrom langflow.custom import Component\nfrom langflow.io import MessageTextInput, Output\n\nclass MyComponent(Component):\n    display_name = \"My Component\"\n    description = \"What it does\"\n    icon = \"component-icon\"  # Lucide icon name or custom\n\n    inputs = [\n        MessageTextInput(name=\"input_value\", display_name=\"Input\"),\n    ]\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"process\"),\n    ]\n\n    def process(self) -> Message:\n        # Component logic\n        return Message(text=self.input_value)\n```\n\n### Component Testing\nTests go in `src/backend/tests/unit/components/`. Use base classes:\n- `ComponentTestBaseWithClient` - Components needing API access\n- `ComponentTestBaseWithoutClient` - Pure logic components\n\nRequired fixtures: `component_class`, `default_kwargs`, `file_names_mapping`\n\n## Frontend Development\n\n- **React 19** + TypeScript + Vite\n- **Zustand** for state management\n- **@xyflow/react** for graph visualization\n- **Tailwind CSS** for styling\n\n### Custom Icons\n1. Create SVG component in `src/frontend/src/icons/YourIcon/`\n2. Export with `forwardRef` and `isDark` prop support\n3. Add to `lazyIconImports.ts`\n4. Set `icon = \"YourIcon\"` in Python component\n\n## Testing Notes\n\n- `@pytest.mark.api_key_required` - Tests requiring external API keys\n- `@pytest.mark.no_blockbuster` - Skip blockbuster plugin\n- Database tests may fail in batch but pass individually\n- Pre-commit hooks require `uv run git commit`\n- Always use `uv run` when running Python commands\n- When running tests inside a sub-package (e.g. `langflow-base`, `lfx`), sync that package's dev group first: `uv sync --group dev --package langflow-base`. The default `uv sync` only resolves the top-level workspace and may leave dev-only test deps (e.g. `fakeredis`) uninstalled.\n\n### Graph Testing Pattern\n\nProper Graph tests follow this pattern:\n1. Build graph with connected components\n2. Connect them via `.set()` calls\n3. Call `async_start` and iterate over the results\n4. Validate the results\n\n### Testing Best Practices\n\n- Avoid mocking in tests when possible\n- Prefer real integrations for more reliable tests\n\n## Version Management\n```bash\nmake patch v=1.5.0  # Update version across all packages\n```\n\nThis updates: `pyproject.toml`, `src/backend/base/pyproject.toml`, `src/frontend/package.json`\n\n## Pre-commit Workflow\n\nPre-commit hooks run ruff and biome automatically on `git commit`, so manual\nformatting is not required. To avoid an extra commit cycle when you have many\nchanges:\n\n1. Run `make format_backend` once before staging - fixes most ruff issues up front.\n2. Run `uv run git commit` (the `uv run` ensures pre-commit finds the right Python).\n3. If you touched backend code, run `make unit_tests` locally for faster feedback than CI.\n\n## Pull Request Guidelines\n\n- Follow [semantic commit conventions](https://www.conventionalcommits.org/)\n- Reference any issues fixed (e.g., `Fixes #1234`)\n- Ensure all tests pass before submitting\n\n## Documentation\n\nDocumentation uses Docusaurus and lives in `docs/`:\n```bash\ncd docs\nyarn install\nyarn start        # Dev server on port 3000 (prompts for 3001 if 3000 is in use)\n```\n","CLAUDE.md":"# CLAUDE.md\n\n@AGENTS.md\n@.claude/CLAUDE.md\n\nThis project uses [AGENTS.md](https://agents.md/) as the standard for providing context to AI coding agents. The `@AGENTS.md` import above tells Claude Code to load `AGENTS.md` automatically; other tools that natively support `AGENTS.md` will pick it up directly. The `@.claude/CLAUDE.md` import loads the local hard-rules file (gitignored) that mirrors the PostToolUse hook policy.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# AGENTS.md\n\nThis file provides guidance to AI coding agents when working with code in this repository.\n\n## Project Overview\n\nLangflow is a visual workflow builder for AI-powered agents. It has a Python/FastAPI backend, React/TypeScript frontend, and a lightweight executor CLI (lfx).\n\n## Prerequisites\n\n- **Python:** 3.10-3.14\n- **uv:** >=0.4 (Python package manager)\n- **Node.js:** >=20.19.0 (v22.12 LTS recommended)\n- **npm:** v10.9+\n- **make:** For build coordination\n\n## Common Commands\n\n### Development Setup\n```bash\nmake init              # Install all dependencies + pre-commit hooks\nmake run_cli           # Build and run Langflow (http://localhost:7860)\nmake run_clic          # Clean build and run (use when frontend issues occur)\n```\n\n### Development Mode (Hot Reload)\n```bash\nmake backend           # FastAPI on port 7860 (terminal 1)\nmake frontend          # Vite dev server on port 3000 (terminal 2)\n```\n\nFor component development, enable dynamic loading:\n```bash\nLFX_DEV=1 make backend                    # Load all components dynamically\nLFX_DEV=mistral,openai make backend       # Load only specific modules\n```\n\n### Code Quality\n```bash\nmake format_backend    # Format Python (ruff) - run FIRST before lint\nmake format_frontend   # Format TypeScript (biome)\nmake format            # Both\nmake lint              # mypy type checking\n```\n\n### Testing\n```bash\nmake unit_tests                    # Backend unit tests (pytest, parallel)\nmake unit_tests async=false        # Sequential tests\nuv run pytest path/to/test.py      # Single test file\nuv run pytest path/to/test.py::test_name  # Single test\n\nmake test_frontend                 # Jest unit tests\nmake tests_frontend                # Playwright e2e tests\n```\n\n### Database Migrations\n```bash\nmake alembic-revision message=\"Description\"  # Create migration\nmake alembic-upgrade                         # Apply migrations\nmake alembic-downgrade                       # Rollback one version\n```\n\n## Architecture\n\n### Monorepo Structure\n```\nsrc/\n├── backend/\n│   ├── base/langflow/     # Core backend package (langflow-base)\n│   │   ├── api/           # FastAPI routes (v1/, v2/)\n│   │   ├── components/    # Built-in Langflow components\n│   │   ├── services/      # Service layer (auth, database, cache, etc.)\n│   │   ├── graph/         # Flow graph execution engine\n│   │   └── custom/        # Custom component framework\n│   └── tests/             # Backend tests\n├── frontend/              # React/TypeScript UI\n│   └── src/\n│       ├── components/    # UI components\n│       ├── stores/        # Zustand state management\n│       └── icons/         # Component icons\n└── lfx/                   # Lightweight executor CLI\n```\n\n### Key Packages\n- **langflow**: Main package with all integrations\n- **langflow-base**: Core framework (api, services, graph engine)\n- **lfx**: Standalone CLI for running flows (`lfx serve`, `lfx run`)\n\n### Service Layer\nBackend services in `src/backend/base/langflow/services/`:\n- `auth/` - Authentication\n- `authorization/` - Authorization (RBAC) plugin layer — see below\n- `database/` - SQLAlchemy models and migrations\n- `cache/` - Caching layer\n- `storage/` - File storage\n- `tracing/` - Observability integrations\n\n### Authorization (RBAC)\n\nAuthorization is a pluggable layer separate from authentication:\n\n- **OSS** ships the interface (`BaseAuthorizationService` in `lfx`) + a pass-through implementation (`LangflowAuthorizationService`) + the `authz_*` and `casbin_rule` DB schema + route guards.\n- Implementations register via the `lfx.services` entry point `authorization_service` in `lfx.toml` (same pattern as the SSO `auth_service`). A registered plugin reads the `authz_*` admin tables and writes compiled rules to `casbin_rule`.\n\nDefault is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.\n\nRoute guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):\n- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute\n- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`\n- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`\n- `ensure_knowledge_base_permission(user, KnowledgeBaseAction.*, kb_name=..., kb_user_id=...)`\n- `ensure_variable_permission(user, VariableAction.*, variable_id=..., variable_user_id=...)`\n- `ensure_file_permission(user, FileAction.*, file_id=..., file_user_id=...)`\n- `ensure_share_permission(user, ShareAction.*, share_id=..., share_user_id=...)`\n- `filter_visible_resources(user, resource_type=..., candidates=..., act=...)` — list-endpoint filter; safe no-op in OSS\n\nThe enforcement request shape is `(subject, domain, object, action)`:\n- subject = `user:{uuid}`\n- domain = `project:{uuid}` → `workspace:{uuid}` → `*` (resolved by `_resolve_flow_domain`; the more specific domain wins so project-scoped grants match directly while workspace-scoped grants still flow down via plugin-side role inheritance)\n- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.\n- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`\n\n**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.\n\n**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.\n\n**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.\n\n**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `\"{resource}:{action}\"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.\n\n## Component Development\n\nComponents live in `src/backend/base/langflow/components/`. To add a new component:\n\n1. Create component class inheriting from `Component`\n2. Define `display_name`, `description`, `icon`, `inputs`, `outputs`\n3. Add to `__init__.py` (alphabetical order)\n4. Run with `LFX_DEV=1 make backend` for hot reload\n\n**IMPORTANT:** Changing a component's class name is a breaking change and should never be done. The class name serves as an identifier used to match components in saved flows and to flag them for updates in the UI. Renaming it will break existing flows that use that component.\n\n### Component Structure\n```python\nfrom langflow.custom import Component\nfrom langflow.io import MessageTextInput, Output\n\nclass MyComponent(Component):\n    display_name = \"My Component\"\n    description = \"What it does\"\n    icon = \"component-icon\"  # Lucide icon name or custom\n\n    inputs = [\n        MessageTextInput(name=\"input_value\", display_name=\"Input\"),\n    ]\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"process\"),\n    ]\n\n    def process(self) -> Message:\n        # Component logic\n        return Message(text=self.input_value)\n```\n\n### Component Testing\nTests go in `src/backend/tests/unit/components/`. Use base classes:\n- `ComponentTestBaseWithClient` - Components needing API access\n- `ComponentTestBaseWithoutClient` - Pure logic components\n\nRequired fixtures: `component_class`, `default_kwargs`, `file_names_mapping`\n\n## Frontend Development\n\n- **React 19** + TypeScript + Vite\n- **Zustand** for state management\n- **@xyflow/react** for graph visualization\n- **Tailwind CSS** for styling\n\n### Custom Icons\n1. Create SVG component in `src/frontend/src/icons/YourIcon/`\n2. Export with `forwardRef` and `isDark` prop support\n3. Add to `lazyIconImports.ts`\n4. Set `icon = \"YourIcon\"` in Python component\n\n## Testing Notes\n\n- `@pytest.mark.api_key_required` - Tests requiring external API keys\n- `@pytest.mark.no_blockbuster` - Skip blockbuster plugin\n- Database tests may fail in batch but pass individually\n- Pre-commit hooks require `uv run git commit`\n- Always use `uv run` when running Python commands\n- When running tests inside a sub-package (e.g. `langflow-base`, `lfx`), sync that package's dev group first: `uv sync --group dev --package langflow-base`. The default `uv sync` only resolves the top-level workspace and may leave dev-only test deps (e.g. `fakeredis`) uninstalled.\n\n### Graph Testing Pattern\n\nProper Graph tests follow this pattern:\n1. Build graph with connected components\n2. Connect them via `.set()` calls\n3. Call `async_start` and iterate over the results\n4. Validate the results\n\n### Testing Best Practices\n\n- Avoid mocking in tests when possible\n- Prefer real integrations for more reliable tests\n\n## Version Management\n```bash\nmake patch v=1.5.0  # Update version across all packages\n```\n\nThis updates: `pyproject.toml`, `src/backend/base/pyproject.toml`, `src/frontend/package.json`\n\n## Pre-commit Workflow\n\nPre-commit hooks run ruff and biome automatically on `git commit`, so manual\nformatting is not required. To avoid an extra commit cycle when you have many\nchanges:\n\n1. Run `make format_backend` once before staging - fixes most ruff issues up front.\n2. Run `uv run git commit` (the `uv run` ensures pre-commit finds the right Python).\n3. If you touched backend code, run `make unit_tests` locally for faster feedback than CI.\n\n## Pull Request Guidelines\n\n- Follow [semantic commit conventions](https://www.conventionalcommits.org/)\n- Reference any issues fixed (e.g., `Fixes #1234`)\n- Ensure all tests pass before submitting\n\n## Documentation\n\nDocumentation uses Docusaurus and lives in `docs/`:\n```bash\ncd docs\nyarn install\nyarn start        # Dev server on port 3000 (prompts for 3001 if 3000 is in use)\n```\n","category":"root","tokens":2795},{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\n@AGENTS.md\n@.claude/CLAUDE.md\n\nThis project uses [AGENTS.md](https://agents.md/) as the standard for providing context to AI coding agents. The `@AGENTS.md` import above tells Claude Code to load `AGENTS.md` automatically; other tools that natively support `AGENTS.md` will pick it up directly. The `@.claude/CLAUDE.md` import loads the local hard-rules file (gitignored) that mirrors the PostToolUse hook policy.\n","category":"root","tokens":107}]}