Skip to content

Multi tenancy implementation - #98

Open
GokulVGAot wants to merge 12 commits into
AOT-Technologies:mainfrom
GokulVGAot:multi-tenancy-implementation
Open

Multi tenancy implementation#98
GokulVGAot wants to merge 12 commits into
AOT-Technologies:mainfrom
GokulVGAot:multi-tenancy-implementation

Conversation

@GokulVGAot

Copy link
Copy Markdown
Contributor

Description

Adds header-based multi-tenancy and a runtime in-memory connector config API while keeping the existing single-tenant YAML flow working.

Core

  • Trusted tenant identity from X-Tenant-ID / NW_TENANT_ID_HEADER (optional env pin, JWT tenant_id fallback, else __default__)
  • ConnectorConfigStore with named configs per (tenant, connector), default selection, redacted reads, and fail-closed lookups
  • YAML bootstrap into __default__ on startup (NW_CONFIG_BOOTSTRAP_YAML)
  • Async factory get(tenant_id, config_name) with a three-part cache key, invalidation on writes, config injection on BaseConnector, and tenant-scoped secrets (NW_{TENANT}_{CONNECTOR}_{KEY})

Bindings

  • REST: /v1/... config CRUD; invoke uses header tenant + optional config_name; tenant prepended to the REST rate-limit key
  • gRPC: optional config_name on InvokeRequest; tenant from metadata
  • MCP: session/header/NW_TENANT_ID tenant pin; optional config_name tool argument

Playground

  • Tenancy bar (Tenant ID + Config name) on connector runs
  • Google Drive runtime config CRUD panel wired to the shared REST store for manual fail-closed → provision → invoke testing

Tests

  • New tests/test_multitenancy.py covering store lifecycle, identity, factory isolation/invalidation, fail-closed 403s, and direct integration
  • Existing REST/MCP tests updated for the async tenant-aware path

Intentionally deferred: gRPC streaming and store.export().

Related Issue

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / chore

Checklist

  • I ran the quality checks locally (ruff, mypy, bandit, pytest).
  • New files include the required SPDX/REUSE license header.
  • I added or updated tests where appropriate.
  • I updated documentation where appropriate.
  • My commits use a correctly configured git identity (real name and email).
  • All my commits are signed off (git commit -s) per the DCO.

@GokulVGAot
GokulVGAot requested a review from a team as a code owner July 22, 2026 04:50
Comment thread tests/test_multitenancy.py Fixed
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
@GokulVGAot
GokulVGAot force-pushed the multi-tenancy-implementation branch from f8b5e9d to 0c9c5b6 Compare July 29, 2026 08:01
Comment thread playground/scenarios.py Fixed
Comment thread src/bindings/rest_api/app.py Fixed
Comment thread src/agents/providers/groq_provider.py Fixed
Comment thread src/agents/providers/openai_provider.py Fixed
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Comment thread playground/scenarios.py Fixed
Comment thread src/agents/llm_factory.py Fixed
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Comment thread src/agents/llm_factory.py Fixed
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Comment thread src/bindings/factory.py Outdated
Comment thread src/bindings/rest_api/app.py Outdated
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Comment thread tests/test_factory_and_rest.py Outdated
try:
for _ in range(40):
await factory.get("http_generic", tenant_id=DEFAULT_TENANT)
except BaseException as exc: # noqa: BLE001
Comment thread tests/test_factory_and_rest.py Outdated
try:
for _ in range(40):
factory.invalidate_configs(DEFAULT_TENANT, "http_generic", ["default"])
except BaseException as exc: # noqa: BLE001
Comment thread tests/test_factory_and_rest.py Outdated
try:
for _ in range(40):
factory._default_instance("http_generic")
except BaseException as exc: # noqa: BLE001
Signed-off-by: gokul-aot <gokul.vg@aot-technologies.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants