Skip to content

FEAT: Support editing config from GUI - #2486

Open
Behnam (behnam-o) wants to merge 20 commits into
microsoft:mainfrom
behnam-o:custom-initializers
Open

FEAT: Support editing config from GUI#2486
Behnam (behnam-o) wants to merge 20 commits into
microsoft:mainfrom
behnam-o:custom-initializers

Conversation

@behnam-o

@behnam-o Behnam (behnam-o) commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Problem

Previously, changing configuration, environment values, or initializer code in a deployed CoPyRIT instance required direct access to its filesystem, container, or deployment configuration. Changes could also be lost when the container restarted.

This change provides a persistent, browser-based workflow for managing those resources and applying changes without redeploying the application.

Summary

Adds GUI-based management of PyRIT backend configuration, enabling deployed CoPyRIT instances to be configured without direct filesystem or container access.

  • Adds support for using a .pyrit_conf Azure Blob Storage in addition to local files.
  • Adds backend APIs and a GUI editor for .pyrit_conf (regardless of whether it's a local file or blob)
  • Adds backend APIs and a GUI editor for dotenv and Azure Key Vault environment sources.
  • Adds support for persisting custom initializer scripts in Azure Blob Storage in addition to the default local path
  • Adds GUI for creating and removing custom initializers (backend already exists - just missed a "list" API)
  • Updates Docker and Bicep deployment configuration to let us use a custom conf file (to take advantage of the new feature that supports pointing to a blob)
  • Updates the docker and deployment configuration to better handle AKV-referenced env vars (instead of letting ARM read the AKV, set its content to an env var, which is then read in start.sh and written to a local file in the container, just let start.sh pass the AKV reference directly in the generate conf file)
  • Introduce an admin group, and requires the user be a member of that to access the conf management APIs.

Comment thread pyrit/memory/memory_interface.py Outdated
Comment thread pyrit/backend/main.py Outdated
Comment thread pyrit/backend/services/initializer_service.py Outdated
Comment thread frontend/src/components/Initializers/CustomInitializers.tsx Outdated
@behnam-o Behnam (behnam-o) changed the title FEAT: Support creating custom initializers and applying them via GUI FEAT: Support editing config from GUI Aug 26, 2026
@behnam-o
Behnam (behnam-o) marked this pull request as draft August 26, 2026 21:50
@behnam-o
Behnam (behnam-o) marked this pull request as ready for review August 27, 2026 17:28
Comment thread pyrit/backend/routes/initializers.py
Comment thread pyrit/backend/routes/configuration.py
Comment thread pyrit/registry/components/initializer_registry.py
Comment thread pyrit/backend/main.py Outdated
Comment thread frontend/src/components/Sidebar/Navigation.tsx
Comment thread docker/start.sh
fi
} >"$RUNTIME_CONFIG"

CONFIG_FILE="${PYRIT_CONFIG_FILE:-$RUNTIME_CONFIG}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With pyritConfigFileUri empty (the default in both param files), PUT /api/config writes /tmp/pyrit_runtime.yaml, and start.sh regenerates that file on startup. Does a GUI edit survive the restart the UI suggests, or is there a persistence path I'm missing?

Comment thread infra/README.md
generate the existing minimal config from `sqlServerFqdn` and
`pyritInitializer` at container startup.

Grant the managed identity `Storage Blob Data Contributor` on the config blob,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This lists Storage Blob Data Contributor for the config blob. The env editor also writes to Key Vault via set_secret, and the AKV path reads from it at runtime — should the KV role be listed here too? Post-Deployment still has # Note: Key Vault Secrets User is NOT required.


script_path = self._get_custom_scripts_dir() / f"{name}.py"
script_path.unlink(missing_ok=True)
self._get_custom_storage().delete_script(name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The runtime class is dropped just above this, so if delete_script fails the script stays in storage and the retry raises KeyError — and it gets re-registered on next boot. Should storage be deleted first?

class TestCustomInitializerRoutes:
"""Tests for runtime custom initializer routes."""

def test_post_returns_403_when_custom_initializers_disabled(self, client: TestClient) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The two *_403_when_custom_initializers_disabled tests look like they dropped off here — the 422 name-validation ones stayed. Intentional? Flagging since that flag is the kill-switch.

if not isinstance(yaml_data, dict):
raise ValueError("Configuration content must be a non-empty YAML mapping.")
try:
ConfigurationLoader.from_dict(yaml_data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Saving allow_custom_initializers: "false" (quoted) passes validation, but it stays a string and reads truthy at startup — so the flag ends up enabled. Unquoted false/no/off are fine. Might be worth enforcing the type here

Comment thread infra/main.bicep

@description('Object ID of the Entra security group allowed to manage backend configuration')
@minLength(1)
param adminGroupObjectId string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

adminGroupObjectId is required with no default, but neither az deployment group create in gui-deploy.yml passes it (lines 154-168 and 237-251), and this PR doesn't touch that file. Test and prod stages might fail template validation after merge

@varunj-msft

Copy link
Copy Markdown
Contributor

Left a few notes — the video made this much easier to follow! Only two I'd actually give weight on: gui-deploy.yml doesn't pass the new required adminGroupObjectId (param files and deploy_instance.py all got it, just not the pipeline), and a quoted "false" on allow_custom_initializers reads truthy. The rest are minor/follow-up material and non-blocking!

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