Skip to content

feat(web): managed-mode connection policy foundation (#1273) - #1278

Open
Asymtode712 wants to merge 8 commits into
TabularisDB:mainfrom
Asymtode712:feat/web-managed-mode-connections
Open

Asymtode712 wants to merge 8 commits into
TabularisDB:mainfrom
Asymtode712:feat/web-managed-mode-connections

Conversation

@Asymtode712

@Asymtode712 Asymtode712 commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Goal

An opt-in managed mode for Tabularis Web where administrators supply the connections. Browser users can query within the database account's privileges, but cannot change destinations, create or remove connections, or obtain credentials. The mode is off by default and leaves standalone Web and desktop behaviour unchanged.

Where this builds on existing code

  • AuthorizationLevel and the checks in transport/web/rpc_connection_policy.rs already limit what a remote session can do. Managed mode extends that approach rather than adding a parallel mechanism.
  • Connection mutations already go through ConnectionCommand and ConnectionFilesCommand in application/.
  • SessionNegotiation in transport/web/contract.rs already tells the frontend what the session can do.

Policy modes

  • standalone (default): today's behaviour. Every check passes.
  • managed-only: every connection on the instance is managed.
  • Mixed mode is not part of the first implementation. Policy decisions read a persisted per-connection managed marker (provisioned connections carry it), so mixed mode later only changes how the instance default is applied. It needs no new storage or checks.

Shared policy boundary with #1274

Who can use which connection

  • Initial scope: every authorized browser user of an instance can see and use all managed connections on that instance. There are no per-user or per-connection grants.
  • Access control is the existing web authentication (password or proxy). Which database objects a user can reach is limited by the database account's privileges, which remain a deployment responsibility.
  • Separation between groups of users is done with a separate instance per group.
  • Per-user grants and multi-tenant isolation are not claimed. They would need their own access-rule design.

What browser sessions can do in managed mode

Allowed: open managed connections, browse metadata, run queries within the account's privileges, and per-session state such as selected databases.

Denied (enforced in the backend, never only in the UI):

  • save_connection, update_connection, delete_connection, duplicate_connection.
  • test_connection and list_databases with caller-supplied params.
  • apply_connection_import, apply_prepared_tabularis_import, export_connections_file.
  • Any override of driver, host, port, database, tunnel, proxy or plugin settings on a managed connectionId.

Branding and organization:

  • Branding (appearance and icons) and organization (groups and tags) have separate operator settings. Both default to denied in managed mode, and an operator can allow either on its own.
  • Groups and tags are treated as shared instance state. Personal, per-user organization is out of scope until there is a user identity and storage for it.
  • Step 1 confirms where groups, tags and appearance are stored today, and the docs state the result explicitly.

Credential exposure

  • One redaction step in the application layer for connection responses, exports and events.
  • Driver and tunnel error messages are scrubbed before they reach the browser.

Denial error code

Policy denials return a stable machine-readable code (for example managed_connection_denied). The frontend maps it to a translated message in every locale, and tests assert the code, not the text.

Frontend

Session negotiation gains a managed flag and the effective connection-management permissions. The UI hides unavailable actions and explains that the connections are externally managed. It only presents. The backend enforces. New strings go into every locale under packages/web-ui/src/i18n/locales/.

Test plan

  • Policy matrix (backend): a table-driven test covers every connection-related RPC command, including alternate paths (import, duplicate, export, test, list databases, appearance, groups, tags). It checks browser and provisioning actors in standalone and managed-only modes, and with the branding and organization switches on and off. A guard test fails if a new connection command has no policy entry.
  • Bypass attempts: direct RPC calls that override destination fields on a managed connectionId, import payloads that smuggle in a connection, and a client-forged managed flag.
  • Credential exposure: fixtures with distinctive secret values, asserting they appear in no response, export, event or error body.
  • Error code: denials return the dedicated code on every denied path.
  • Frontend: capability handling (actions hidden, translated message shown, standalone unchanged) and locale key parity.
  • Regression: existing Web and desktop tests are unchanged. cargo check --no-default-features and the repository checks stay clean.

Rollout

  1. Policy type, instance config (standalone / managed-only), per-connection managed marker, the branding and organization switches (default denied), confirmation of where groups, tags and appearance are stored, and default-off tests.
  2. Backend denials with the dedicated error code, credential redaction, the policy matrix, and the bypass and exposure tests.
  3. Capabilities in session negotiation, frontend handling of the error code and hidden actions, and strings in every locale.
  4. [Feat]: Web API for provisioning and maintaining managed connections #1274 provisioning API as the only authorised way to change managed connections.

Out of scope

Per-user grants, multi-tenant isolation, personal (per-user) groups and tags, mixed mode, network restrictions and database privileges. The last two remain deployment responsibilities.

@Asymtode712
Asymtode712 marked this pull request as draft October 24, 2026 06:33
@Asymtode712 Asymtode712 changed the title docs(web): plan managed-mode connection policy (https://github.com/TabularisDB/tabularis/issues/1273) docs(web): plan managed-mode connection policy (#1273 ) Oct 24, 2026
@Asymtode712

Copy link
Copy Markdown
Contributor Author

@debba please check this implemention plan, also:

Open questions for review

  • Should groups, tags and appearance stay editable by browser users in managed mode?
  • Should the managed marker be stored per connection (as proposed) or derived from a server-wide flag? The former allows mixed instances later.
  • Should denied commands return a dedicated error code so the frontend can show the managed-mode message without parsing text?

@Asymtode712 Asymtode712 changed the title docs(web): plan managed-mode connection policy (#1273 ) docs(web): plan managed-mode connection policy (#1273) Oct 24, 2026
@NewtTheWolf

Copy link
Copy Markdown
Collaborator

My thoughts on the open questions:

  1. Groups, tags and appearance: This should be configurable by the operator, with editing disabled by default in managed mode. Branding and organizational settings should be separate options, so an operator can preserve their branding while still allowing users to organize connections.

  2. Managed marker: I’d use both an instance-level policy and a per-connection managed marker. Operators should be able to choose between:

    • Managed connections only.
    • Mixed mode: protected managed connections alongside user-created connections.
    • Normal standalone behavior.

    For integrations like Plesk, the operator needs to enforce managed-only mode. Another operator should be free to allow mixed mode. The instance policy must enforce this regardless of whether an individual connection has the marker; the marker protects managed connections in mixed mode.

  3. Error codes: Yes, please use a dedicated error code for policy denials, so the frontend can show a translated managed-mode message without parsing error text.

@Asymtode712

Copy link
Copy Markdown
Contributor Author

@debba can you please share your thoughts on this, so that I can start with the implementation

@debba

debba commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

@Asymtode712 @NewtTheWolf thanks for laying this out. I agree with Newt's suggestions.

For groups, tags and appearance, I'd keep branding controls separate from connection organization. Editing shared settings should be disabled by default in managed mode and configurable by the operator. If groups and tags are personal preferences visible only to the current user, I'm happy to allow those. Let's make their scope and storage explicit.

I also agree with using both an instance-level policy and a per-connection managed marker. For the first implementation, I'd start with normal standalone behavior and managed-only mode, while keeping the design ready for mixed mode. We don't need to ship all three at once.

A dedicated error code for policy denials makes sense too, so the frontend can show a translated message reliably.

One more thing I'd like us to clarify before implementation: who can see and use each managed connection? Preventing edits and hiding credentials doesn't by itself define which users can access which databases. If the initial scope is that all authorized browser users can use all managed connections, let's state that explicitly; otherwise, we need to define the access rules.

With those points clarified, I'm happy with this direction.

@Asymtode712

Copy link
Copy Markdown
Contributor Author

@debba Thanks, this is useful. I've updated the plan for each point.

1. Branding vs. organization (appearance, groups, tags)

  • Separate controls. Branding (appearance and icons) and organization (groups and tags) get separate operator settings in managed mode. Each defaults to denied for browser sessions, and an operator can allow either on its own.
  • Personal preferences are out of scope for now. The initial scope has no per-user identity, since each instance serves one group of users with identical rights. There is nowhere to store groups or tags that only the current user sees. I'd rather not imply per-user behaviour we can't back, so groups and tags are treated as shared instance state, and personal organization waits until there is a user identity and storage for it.
  • Storage scope. I'll confirm in step 1 where groups, tags and appearance are stored today, and state the result explicitly in the PR and docs.

2. Policy modes

  • The first implementation ships two modes: standalone (today's behaviour, the default) and managed-only (every connection on the instance is managed).
  • It includes both the instance-level policy and the per-connection managed marker. Policy decisions read the marker, so mixed mode later only changes how the instance default is applied. It needs no new storage or checks, and it isn't shipped now.

3. Denial error code

  • Policy denials return a stable machine-readable code (for example managed_connection_denied), and the frontend maps it to a translated message in every locale.
  • The policy matrix tests assert the code, not message text.

4. Who can use which connection

  • Initial scope: every authorized browser user of an instance can see and use all managed connections on that instance. There are no per-user or per-connection grants.
  • Access control is the existing web authentication (password or proxy). Which database objects a user can reach is limited by the database account's privileges, which stay a deployment responsibility.
  • Separation between groups of users is done with a separate instance per group, as in the issue.
  • This is stated in the PR description and in the user documentation. Per-user grants and multi-tenant isolation are not claimed and would need their own access-rule design.

Updated rollout

  1. Policy type, instance config (standalone / managed-only), per-connection managed marker, the two operator switches (organization and branding, default denied), confirmation of where groups, tags and appearance are stored, and default-off tests.
  2. Backend denials with the dedicated error code, credential redaction, the policy matrix and bypass/exposure tests.
  3. Capabilities in session negotiation, frontend handling of the error code and hidden actions, and strings in every locale.
  4. [Feat]: Web API for provisioning and maintaining managed connections #1274 provisioning API as the only authorised way to change managed connections.

If this matches what you had in mind, I'll start on step 1.

@NewtTheWolf

Copy link
Copy Markdown
Collaborator

Thanks for spelling out the access scope and shared settings. This matches the direction I had in mind. Please go ahead with step 1: standalone by default, managed-only as the opt-in mode, separate branding and organization switches defaulting to denied, and the persisted managed marker.

In managed-only mode, the instance policy must protect every connection even if its marker is missing or false. Please make that case explicit in the policy tests. The marker can support a later mixed mode; we can review that mode separately when it is implemented.

All authenticated users sharing the instance's managed connections, with separate instances for separate groups, is clear for this first scope. Please keep that limitation prominent in the docs. No need to wait for another design round before starting step 1.

@Asymtode712
Asymtode712 marked this pull request as ready for review November 10, 2026 10:06
Comment thread src-tauri/src/transport/desktop/mod.rs Resolved
@kilo-code-bot

kilo-code-bot Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Notes

  • The new commit only documents the --connection-mode, --managed-allow-branding and --managed-allow-organization options in docs/web/WEB_MODE_OPERATIONS.md and adds them to the CLI-coverage check in scripts/web/check-web-ui-documentation.test.mjs. The docs match src-tauri/src/cli.rs and cli/tests.rs: the managed-allow-* flags require --connection-mode, and standalone is the default.
  • The earlier cargo fmt churn suggestion no longer applies: those files (transport/desktop/mod.rs, models_tests.rs, sandbox_protocol.rs, rpc_tests.rs, mysql_columns.rs) are no longer part of the PR's changed files.
  • Not reviewed: I did not run the tests or build.
Files Reviewed (2 files)
  • docs/web/WEB_MODE_OPERATIONS.md
  • scripts/web/check-web-ui-documentation.test.mjs
Previous Review Summaries (2 snapshots, latest commit f49c99f)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit f49c99f)

Status: 1 Issue Found | Recommendation: Merge (optional cleanup)

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
src-tauri/src/transport/desktop/mod.rs 18 Unrelated cargo fmt churn (module reorder here, plus models_tests.rs, sandbox_protocol.rs, transport/web/rpc_tests.rs, tests/mysql_columns.rs). Consider moving it to a separate commit.

Notes

  • The previous CRITICAL finding is fixed: managed: false was added to the SavedConnection literals in tests/mysql_table_transfer.rs, tests/postgres_integration/table_transfer.rs and tests/sqlite_table_transfer_reals.rs. No other SavedConnection literals remain in src-tauri/tests.
  • The new commit also adds further cargo fmt reformatting (models_tests.rs, sandbox_protocol.rs, rpc_tests.rs, mysql_columns.rs), which feeds the suggestion above. The GUEST_CSP reformat in sandbox_protocol.rs keeps the same string value.
  • Not reviewed: I did not run the tests or build, so I could not confirm that cargo check or the test suites pass.

Fix these issues in Kilo Cloud

Files Reviewed (8 files)
  • src-tauri/src/models_tests.rs
  • src-tauri/src/transport/desktop/mod.rs - 1 issue
  • src-tauri/src/transport/desktop/sandbox_protocol.rs
  • src-tauri/src/transport/web/rpc_tests.rs
  • src-tauri/tests/mysql_columns.rs
  • src-tauri/tests/mysql_table_transfer.rs
  • src-tauri/tests/postgres_integration/table_transfer.rs
  • src-tauri/tests/sqlite_table_transfer_reals.rs

Previous review (commit 7f00f11)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

CRITICAL

File Line Issue
src-tauri/tests/mysql_table_transfer.rs, src-tauri/tests/postgres_integration/table_transfer.rs, src-tauri/tests/sqlite_table_transfer_reals.rs 77 / 48 / 71 SavedConnection gained a required managed: bool field (src-tauri/src/models.rs). These three integration-test files build SavedConnection { .. } literals without it and are not touched by this PR, so they fail to compile (E0063) when the src-tauri/tests targets are built, for example by cargo test. Add managed: false to each literal. These files are outside the diff, so this is reported here rather than inline.

SUGGESTION

File Line Issue
src-tauri/src/transport/desktop/mod.rs 18 Unrelated cargo fmt churn (module reorder here, plus models_tests.rs, sandbox_protocol.rs, transport/web/rpc_tests.rs, tests/mysql_columns.rs). Consider moving it to a separate commit.

Notes

  • The PR title says "docs", but the second commit adds code: ManagedConnectionPolicy, the --connection-mode, --managed-allow-branding and --managed-allow-organization CLI flags, and the managed field on SavedConnection. The policy is wired into RuntimeApplicationApi but nothing calls authorize yet. This matches the docs, which say enforcement is not active, so managed-only is currently a no-op.
  • Not reviewed: I did not run the tests or build, so I could not confirm that cargo check or the existing test suites pass. Apart from the three integration-test files above, the other SavedConnection literals in src-tauri/src appear to be updated.
  • The managed marker is not carried by team_share::VaultEntry or by the connection import paths. This does not matter while enforcement is inactive, but it should be settled in the enforcement step.

Fix these issues in Kilo Cloud

Files Reviewed (33 files)
  • docs/web/README.md
  • docs/web/WEB_MANAGED_MODE.md
  • packages/web-ui/src/contexts/DatabaseContext.ts
  • src-tauri/src/application/api.rs
  • src-tauri/src/application/connection_files_tests.rs
  • src-tauri/src/application/connections.rs
  • src-tauri/src/application/connections_tests.rs
  • src-tauri/src/application/generic_exports_tests.rs
  • src-tauri/src/application/managed_policy.rs
  • src-tauri/src/application/managed_policy/tests.rs
  • src-tauri/src/application/mod.rs
  • src-tauri/src/application/table_transfers_tests.rs
  • src-tauri/src/application/tunnels_tests.rs
  • src-tauri/src/cli.rs
  • src-tauri/src/cli/tests.rs
  • src-tauri/src/commands.rs
  • src-tauri/src/connection_cache_tests.rs
  • src-tauri/src/connection_import/analyzer.rs
  • src-tauri/src/connection_import/convert.rs
  • src-tauri/src/connection_import/tabularis.rs
  • src-tauri/src/connection_migrations_tests.rs
  • src-tauri/src/connection_tags.rs
  • src-tauri/src/export_import_tests.rs
  • src-tauri/src/lib.rs
  • src-tauri/src/models.rs
  • src-tauri/src/models_tests.rs
  • src-tauri/src/persistence_tests.rs
  • src-tauri/src/plugin_secrets_tests.rs
  • src-tauri/src/transport/desktop/mod.rs - 1 issue
  • src-tauri/src/transport/desktop/sandbox_protocol.rs
  • src-tauri/src/transport/web/rpc_tests.rs
  • src-tauri/src/transport/web/tests.rs
  • src-tauri/tests/mysql_columns.rs

Reviewed by claude-sonnet-5.5 · Input: 246 · Output: 1.5K · Cached: 173.2K

Review guidance: REVIEW.md from base branch main

@Asymtode712

Copy link
Copy Markdown
Contributor Author

@NewtTheWolf @debba Step 1 of #1273: the policy type, instance config, persisted marker and default-off tests. Nothing is enforced yet; later steps add backend denials, redaction, session capabilities and the frontend.

  • Add ManagedConnectionPolicy (application/managed_policy) with standalone (default, every check passes) and managed-only modes.
  • In managed-only mode the instance protects every connection, whether its managed marker is true, false or missing. The marker is stored so a later mixed mode needs no new storage.
  • Separate branding and organization switches, both denied by default. Neither opens connection management.
  • Policy denials carry the stable code managed_connection_denied.
  • Persist SavedConnection.managed (missing reads as false, omitted when false). Update preserves it; create, duplicate and import write false.
  • Add --connection-mode, --managed-allow-branding and --managed-allow-organization to tabularis web. The policy is built once at startup and held by RuntimeApplicationApi.
  • Add docs/web/WEB_MANAGED_MODE.md. It states that all authenticated users share every managed connection, that separate groups need separate instances, and where groups, tags and appearance are stored.

Tests cover the standalone default, managed-only with marker false and with no stored marker, the independent switches, the denial code, marker serialization and the CLI defaults.

Not in this step: backend denials, credential redaction, capabilities in session negotiation, frontend handling, the provisioning API (#1274) and mixed mode.

Three steps remain.

  • Step 2: backend enforcement. This covers:
    • Denials with managed_connection_denied on every connection-management path: save, update, delete, duplicate, test and list databases with params, import, export, and appearance, groups and tags (subject to the two switches).
    • Rejecting destination overrides on a managed connectionId.
    • Rejecting a forged managed value from browser payloads or imported files.
    • Credential redaction in responses, exports, events and error messages.
    • The policy matrix with a guard test for new connection commands, plus the bypass and exposure tests.
  • Step 3: frontend. This covers:
    • Managed flag and effective permissions in session negotiation.
    • Handling the error code, and hiding unavailable actions with an explanation.
    • Translated strings in every locale.
    • Frontend and locale-parity tests.
  • Step 4: provisioning API ([Feat]: Web API for provisioning and maintaining managed connections #1274). This is the only authorized way to change managed connections. It is a separate issue, so [Feat]: Web managed mode for administrator-controlled connections #1273 can close without it.

NewtTheWolf
NewtTheWolf previously approved these changes Nov 10, 2026

@NewtTheWolf NewtTheWolf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed c548b1a76cb7582f413b3b31529cb035270fe7d5 as the agreed step-1 foundation. The standalone default, marker-independent managed-only policy, independent branding/organization switches, stable denial code and persisted marker match that limited scope. The production build, server-only check, documentation checks, typecheck and lint pass. The full Rust library suite passes serially (2,531 passed, 19 ignored); the default parallel run failed when registry reconciliation encountered readonly-export-* fixtures, so I am not calling that run green.

Non-blocking follow-ups for the manual check:

  • Update the PR title and opening summary to describe the policy foundation now implemented. The current title still says this is only a planning document.
  • Make --connection-mode help explicitly say that enforcement is not active yet, as the new guide already does. That limitation should be visible where an operator discovers the flag.
  • The ordinary Tabularis JSON import path currently preserves an incoming managed: true through tabularis::apply/apply_export_payload. Foreign-app conversion defaults it to false. Please correct the blanket "import writes false" claim; the planned step-2 forgery/provenance checks must cover both native import paths as well.

This approval covers the foundation only. Backend denials, credential redaction, session capabilities and frontend restrictions are deliberately absent and have not been verified as implemented. Leaving this open for the manual CLI/documentation check before merge.

@Asymtode712 Asymtode712 changed the title docs(web): plan managed-mode connection policy (#1273) feat(web): managed-mode connection policy foundation (#1273) Nov 10, 2026

This branch has not been deployed

No deployments
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