agentgov-core 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentgov_core-0.1.0/.gitignore +54 -0
- agentgov_core-0.1.0/PKG-INFO +32 -0
- agentgov_core-0.1.0/README.md +17 -0
- agentgov_core-0.1.0/pyproject.toml +25 -0
- agentgov_core-0.1.0/src/agentgov_core/__init__.py +0 -0
- agentgov_core-0.1.0/src/agentgov_core/cedar.py +56 -0
- agentgov_core-0.1.0/src/agentgov_core/config.py +128 -0
- agentgov_core-0.1.0/src/agentgov_core/db.py +491 -0
- agentgov_core-0.1.0/src/agentgov_core/envelope.py +156 -0
- agentgov_core-0.1.0/src/agentgov_core/hosts/__init__.py +51 -0
- agentgov_core-0.1.0/src/agentgov_core/hosts/agentcore.py +100 -0
- agentgov_core-0.1.0/src/agentgov_core/hosts/base.py +101 -0
- agentgov_core-0.1.0/src/agentgov_core/hosts/databricks.py +138 -0
- agentgov_core-0.1.0/src/agentgov_core/hosts/litellm.py +95 -0
- agentgov_core-0.1.0/src/agentgov_core/hosts/sigv4.py +104 -0
- agentgov_core-0.1.0/src/agentgov_core/hosts/snowflake.py +151 -0
- agentgov_core-0.1.0/src/agentgov_core/openfga.py +280 -0
- agentgov_core-0.1.0/src/agentgov_core/schema.sql +224 -0
- agentgov_core-0.1.0/src/agentgov_core/servicenow.py +100 -0
- agentgov_core-0.1.0/src/agentgov_core/tuples.py +154 -0
- agentgov_core-0.1.0/src/agentgov_core/urns.py +51 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# --- secrets / credentials (NEVER commit) ---
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
# the template IS committed (no real secrets in it) — keep this negation on its own line;
|
|
5
|
+
# git does NOT support inline comments, so a trailing `# ...` here would break the un-ignore.
|
|
6
|
+
!.env.example
|
|
7
|
+
*.pem
|
|
8
|
+
*.key
|
|
9
|
+
*_secret*
|
|
10
|
+
**/secrets/**
|
|
11
|
+
.mcp.json # contains per-user OAuth; keep a .mcp.json.example instead
|
|
12
|
+
aws-credentials*
|
|
13
|
+
okta-token*
|
|
14
|
+
servicenow-creds*
|
|
15
|
+
|
|
16
|
+
# --- python ---
|
|
17
|
+
__pycache__/
|
|
18
|
+
*.py[cod]
|
|
19
|
+
.venv/
|
|
20
|
+
venv/
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
|
|
24
|
+
# --- node / react ---
|
|
25
|
+
node_modules/
|
|
26
|
+
dist/
|
|
27
|
+
build/
|
|
28
|
+
.vite/
|
|
29
|
+
*.tsbuildinfo
|
|
30
|
+
|
|
31
|
+
# --- terraform (not used, but ignore if anyone adds it) ---
|
|
32
|
+
*.tfstate
|
|
33
|
+
*.tfstate.*
|
|
34
|
+
.terraform/
|
|
35
|
+
|
|
36
|
+
# --- os / editor ---
|
|
37
|
+
.DS_Store
|
|
38
|
+
.idea/
|
|
39
|
+
.vscode/*
|
|
40
|
+
!.vscode/extensions.json
|
|
41
|
+
*.log
|
|
42
|
+
|
|
43
|
+
# --- local dev harness (dev-only; see local/README.md) ---
|
|
44
|
+
local/identity/keys/
|
|
45
|
+
local/*.pid
|
|
46
|
+
local/*.log
|
|
47
|
+
local/.signoz_state.json
|
|
48
|
+
|
|
49
|
+
# --- local data / scratch (synthetic seed IS committed; local dumps are not) ---
|
|
50
|
+
/tmp/
|
|
51
|
+
*.local.json
|
|
52
|
+
seed/**/local-*
|
|
53
|
+
# large binaries — track via LFS or attach separately, not inline
|
|
54
|
+
docs/design/*.docx
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentgov-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Shared invoke-boundary core for agent-governance-demo: host adapters, entitlement/policy resolution, the governance envelope, and the registry Postgres schema. Not published to PyPI yet — built as a local wheel and installed into registry-api/services-workflows images (Phase 5 §5.8).
|
|
5
|
+
Author-email: "receptor.bio" <oss@receptor.bio>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: botocore
|
|
9
|
+
Requires-Dist: cryptography
|
|
10
|
+
Requires-Dist: fastapi
|
|
11
|
+
Requires-Dist: httpx
|
|
12
|
+
Requires-Dist: psycopg[binary]
|
|
13
|
+
Requires-Dist: pyjwt
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# agentgov-core
|
|
17
|
+
|
|
18
|
+
Shared invoke-boundary core for [`agent-governance-demo`](https://github.com/receptor-bio/agent-governance-demo):
|
|
19
|
+
per-platform host adapters (`hosts/`), the governance envelope (`envelope.py`), entitlement/policy
|
|
20
|
+
resolution (`openfga.py`, `cedar.py`), the registry Postgres access layer + schema (`db.py`,
|
|
21
|
+
`schema.sql`), and the shared URN/tuple helpers (`urns.py`, `tuples.py`) that `registry-api` and
|
|
22
|
+
`services/workflows` both consume — replacing the `sys.path` trick `services/workflows/activities.py`
|
|
23
|
+
previously used to reach `registry-api`'s app code directly.
|
|
24
|
+
|
|
25
|
+
**Not published to PyPI yet.** Built as a local wheel in each consuming Dockerfile's multi-stage
|
|
26
|
+
build, and installed with `pip install -e packages/agentgov-core` for local dev. See
|
|
27
|
+
`docs/PHASE_STATUS.md`'s Phase 5 §5.8 entry for the plan to publish it for real via PyPI Trusted
|
|
28
|
+
Publishing (the same OIDC pattern `receptor-bio/axiom-sc` already uses) — deferred, not blocking.
|
|
29
|
+
|
|
30
|
+
Not a general-purpose library: this package's boundary is driven entirely by what
|
|
31
|
+
`services/workflows/activities.py` and `services/registry-api/app/` actually import, not by any
|
|
32
|
+
external API-design goal.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# agentgov-core
|
|
2
|
+
|
|
3
|
+
Shared invoke-boundary core for [`agent-governance-demo`](https://github.com/receptor-bio/agent-governance-demo):
|
|
4
|
+
per-platform host adapters (`hosts/`), the governance envelope (`envelope.py`), entitlement/policy
|
|
5
|
+
resolution (`openfga.py`, `cedar.py`), the registry Postgres access layer + schema (`db.py`,
|
|
6
|
+
`schema.sql`), and the shared URN/tuple helpers (`urns.py`, `tuples.py`) that `registry-api` and
|
|
7
|
+
`services/workflows` both consume — replacing the `sys.path` trick `services/workflows/activities.py`
|
|
8
|
+
previously used to reach `registry-api`'s app code directly.
|
|
9
|
+
|
|
10
|
+
**Not published to PyPI yet.** Built as a local wheel in each consuming Dockerfile's multi-stage
|
|
11
|
+
build, and installed with `pip install -e packages/agentgov-core` for local dev. See
|
|
12
|
+
`docs/PHASE_STATUS.md`'s Phase 5 §5.8 entry for the plan to publish it for real via PyPI Trusted
|
|
13
|
+
Publishing (the same OIDC pattern `receptor-bio/axiom-sc` already uses) — deferred, not blocking.
|
|
14
|
+
|
|
15
|
+
Not a general-purpose library: this package's boundary is driven entirely by what
|
|
16
|
+
`services/workflows/activities.py` and `services/registry-api/app/` actually import, not by any
|
|
17
|
+
external API-design goal.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agentgov-core"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Shared invoke-boundary core for agent-governance-demo: host adapters, entitlement/policy resolution, the governance envelope, and the registry Postgres schema. Not published to PyPI yet — built as a local wheel and installed into registry-api/services-workflows images (Phase 5 §5.8)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "Apache-2.0"}
|
|
11
|
+
# >=3.10, not >=3.11: local dev's .venv (created by run_phase0.sh via `uv venv`) is 3.10.19 — Docker
|
|
12
|
+
# images (python:3.11-slim) satisfy this too, so this is the real floor, not the container version.
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
authors = [{name = "receptor.bio", email = "oss@receptor.bio"}]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"psycopg[binary]",
|
|
17
|
+
"httpx",
|
|
18
|
+
"fastapi",
|
|
19
|
+
"pyjwt",
|
|
20
|
+
"cryptography",
|
|
21
|
+
"botocore",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/agentgov_core"]
|
|
File without changes
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""cedar-agent client (permitio/cedar-agent HTTP API).
|
|
2
|
+
|
|
3
|
+
Runs schemaless locally: we load the policy set and evaluate with an inline context
|
|
4
|
+
carrying { employment, classification }. A Cedar *forbid* overrides *permit*, which is
|
|
5
|
+
how the contractor+RESTRICTED denial works.
|
|
6
|
+
"""
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
from .config import settings
|
|
12
|
+
|
|
13
|
+
TIMEOUT = 10.0
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def split_statements(text: str) -> list[str]:
|
|
17
|
+
"""cedar-agent stores ONE Cedar statement per policy entry. Strip // comments and
|
|
18
|
+
split the policy set on statement terminators (';' only ends statements here)."""
|
|
19
|
+
no_comments = "\n".join(re.sub(r"//.*$", "", line) for line in text.splitlines())
|
|
20
|
+
return [s.strip() + ";" for s in no_comments.split(";") if s.strip()]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Cedar:
|
|
24
|
+
def __init__(self, base_url=None):
|
|
25
|
+
self.base = (base_url or settings.CEDAR_URL).rstrip("/")
|
|
26
|
+
|
|
27
|
+
def load_policies(self, policy_text: str | None = None):
|
|
28
|
+
"""Replace the cedar-agent policy set with services/policy/policies.cedar."""
|
|
29
|
+
if policy_text is None:
|
|
30
|
+
with open(settings.CEDAR_POLICIES_PATH) as f:
|
|
31
|
+
policy_text = f.read()
|
|
32
|
+
# cedar-agent stores a list of {id, content}, one statement each. PUT replaces the set.
|
|
33
|
+
body = [{"id": f"agentgov_{i}", "content": s} for i, s in enumerate(split_statements(policy_text))]
|
|
34
|
+
r = httpx.put(f"{self.base}/v1/policies", json=body, timeout=TIMEOUT)
|
|
35
|
+
r.raise_for_status()
|
|
36
|
+
return r.json()
|
|
37
|
+
|
|
38
|
+
def is_authorized(self, principal_id: str, action: str, resource_id: str, context: dict,
|
|
39
|
+
resource_type: str = "Agent") -> bool:
|
|
40
|
+
"""Return True on Allow. action is 'discover' | 'use'. ``resource_type`` is ``Agent``
|
|
41
|
+
or (Phase 2) ``McpServer`` — the classification/contractor policies are written
|
|
42
|
+
resource-agnostic on purpose, so a governed MCP server is judged by exactly the same
|
|
43
|
+
rules as an agent."""
|
|
44
|
+
body = {
|
|
45
|
+
"principal": f'User::"{principal_id}"',
|
|
46
|
+
"action": f'Action::"{action}"',
|
|
47
|
+
"resource": f'{resource_type}::"{resource_id}"',
|
|
48
|
+
"context": context,
|
|
49
|
+
}
|
|
50
|
+
r = httpx.post(f"{self.base}/v1/is_authorized", json=body, timeout=TIMEOUT)
|
|
51
|
+
r.raise_for_status()
|
|
52
|
+
decision = r.json().get("decision", "Deny")
|
|
53
|
+
return str(decision).lower() == "allow"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
cedar = Cedar()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Runtime configuration for registry-api (env-driven; local defaults for docker-compose)."""
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Settings:
|
|
6
|
+
# Path-prefix awareness (BUILD_PLAN §9). Locally the api answers at /assets/registry/api.
|
|
7
|
+
ROOT_PATH = os.getenv("ROOT_PATH", "/assets/registry/api")
|
|
8
|
+
|
|
9
|
+
# Postgres (spine).
|
|
10
|
+
DB_URL = os.getenv(
|
|
11
|
+
"DB_URL", "postgresql://agentgov:dev@localhost:5432/agentgov"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# OpenFGA (spine). Store is resolved by name so ids need not be plumbed around.
|
|
15
|
+
OPENFGA_API_URL = os.getenv("OPENFGA_API_URL", "http://localhost:8080")
|
|
16
|
+
OPENFGA_STORE_NAME = os.getenv("OPENFGA_STORE_NAME", "agentgov")
|
|
17
|
+
|
|
18
|
+
# Cedar (spine).
|
|
19
|
+
CEDAR_URL = os.getenv("CEDAR_URL", "http://localhost:8180")
|
|
20
|
+
# Local-dev-only convenience default (Cedar.load_policies() reads this when called with no
|
|
21
|
+
# explicit policy_text, which is how registry-api's own startup calls it — see main.py).
|
|
22
|
+
# `services/policy/` is an outer-repo fixture this shared package has no business locating via
|
|
23
|
+
# __file__ trickery (that broke when this module moved out of services/registry-api/app/ in
|
|
24
|
+
# Phase 5 §5.8 — __file__ no longer sits inside the repo tree at a fixed depth). Relative-to-cwd
|
|
25
|
+
# is correct here because run_phase*.sh always launches registry-api's uvicorn process with cwd
|
|
26
|
+
# = services/registry-api (`cd services/registry-api && ... exec uvicorn ...`), never repo root.
|
|
27
|
+
# NOT reachable at all inside the deployed Docker image today (services/policy/ isn't copied in)
|
|
28
|
+
# — pre-existing gap, not introduced or fixed by this change; real deployments would need it
|
|
29
|
+
# added to the Dockerfile if/when this startup call needs to succeed there too.
|
|
30
|
+
CEDAR_POLICIES_PATH = os.getenv("CEDAR_POLICIES_PATH", "../policy/policies.cedar")
|
|
31
|
+
|
|
32
|
+
# Local mock-Okta JWT verification. In real Okta this is a JWKS URL; locally we verify
|
|
33
|
+
# against a PEM public key on disk (no network dependency for the demo). Same cwd-relative
|
|
34
|
+
# reasoning as CEDAR_POLICIES_PATH above — real deployments never hit this path at all, they
|
|
35
|
+
# set JWT_JWKS_URL instead.
|
|
36
|
+
JWT_PUBLIC_KEY_PATH = os.getenv("JWT_PUBLIC_KEY_PATH", "../../local/identity/keys/public.pem")
|
|
37
|
+
JWT_ISSUER = os.getenv("JWT_ISSUER", "https://mock-okta.receptor.local/oauth2")
|
|
38
|
+
JWT_AUDIENCE = os.getenv("JWT_AUDIENCE", "agent-registry")
|
|
39
|
+
# Real Okta: set JWT_JWKS_URL to the authz server's JWKS
|
|
40
|
+
# (https://<domain>/oauth2/<authServerId>/v1/keys) and JWT_ISSUER to its issuer.
|
|
41
|
+
# When set, tokens are verified against Okta's rotating keys instead of the local PEM;
|
|
42
|
+
# when empty (default), the local mock PEM path is used. Nothing else changes.
|
|
43
|
+
JWT_JWKS_URL = os.getenv("JWT_JWKS_URL", "")
|
|
44
|
+
# Allow disabling signature verification only for offline unit poking; default on.
|
|
45
|
+
JWT_VERIFY = os.getenv("JWT_VERIFY", "1") == "1"
|
|
46
|
+
|
|
47
|
+
# OTel. If no OTLP endpoint is set we still emit spans to stdout (ConsoleSpanExporter)
|
|
48
|
+
# so the Phase-0 DoD ("discover emits a trace w/ authz span-event") is observable locally.
|
|
49
|
+
OTLP_ENDPOINT = os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", "")
|
|
50
|
+
OTEL_CONSOLE = os.getenv("OTEL_CONSOLE", "1") == "1"
|
|
51
|
+
SERVICE_NAME = os.getenv("OTEL_SERVICE_NAME", "registry-api")
|
|
52
|
+
# Phase 3 — the SDK default (60s) makes SigNoz-backed usage assertions slow to observe
|
|
53
|
+
# locally; the local test harness shortens this the same way it shortens ACCESS_TTL_MINUTES.
|
|
54
|
+
OTEL_METRIC_EXPORT_INTERVAL_MS = int(os.getenv("OTEL_METRIC_EXPORT_INTERVAL_MS", "60000"))
|
|
55
|
+
|
|
56
|
+
# Phase 1 — Temporal (spine).
|
|
57
|
+
TEMPORAL_ADDRESS = os.getenv("TEMPORAL_ADDRESS", "localhost:7233")
|
|
58
|
+
TEMPORAL_TASK_QUEUE = os.getenv("TEMPORAL_TASK_QUEUE", "agentgov-access")
|
|
59
|
+
|
|
60
|
+
# Phase 1 — ServiceNow. Local dev talks to the mock shim (local/servicenow_mock);
|
|
61
|
+
# swap SERVICENOW_URL to the real PDI once provisioned, hook contract stays identical.
|
|
62
|
+
SERVICENOW_URL = os.getenv("SERVICENOW_URL", "http://localhost:8300")
|
|
63
|
+
# Shared secret authenticating the ServiceNow -> registry-api webhook (dev-only default;
|
|
64
|
+
# real deployments source this from AWS Secrets Manager per CLAUDE.md).
|
|
65
|
+
SERVICENOW_WEBHOOK_SECRET = os.getenv("SERVICENOW_WEBHOOK_SECRET", "local-dev-servicenow-secret")
|
|
66
|
+
|
|
67
|
+
# Governance policy knobs (BUILD_PLAN Phase 1 DoD: "10-min TTL expiry"). Configurable via
|
|
68
|
+
# env so the local test harness can shrink them for fast iteration; the demo/prod default
|
|
69
|
+
# is the real 10-minute TTL. Never accepted as a per-request client override.
|
|
70
|
+
ACCESS_TTL_MINUTES = float(os.getenv("ACCESS_TTL_MINUTES", "10"))
|
|
71
|
+
ACCESS_SLA_MINUTES = float(os.getenv("ACCESS_SLA_MINUTES", "60"))
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------- Phase 1 seam: real ServiceNow
|
|
74
|
+
# Table-API auth for the real PDI (docs/setup/servicenow.md §4/§6). Empty = the local mock,
|
|
75
|
+
# which needs no auth and returns bare records; set both to switch to the real instance.
|
|
76
|
+
SERVICENOW_USER = os.getenv("SERVICENOW_USER", "")
|
|
77
|
+
SERVICENOW_PASSWORD = os.getenv("SERVICENOW_PASSWORD", "")
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------- Phase 2: four hosts
|
|
80
|
+
# 'mock' points every adapter at local/hosts_mock (same contract, no cloud spend);
|
|
81
|
+
# 'real' points at the platforms themselves. Only URLs/credentials differ — BUILD_PLAN §Phase 2.
|
|
82
|
+
HOSTS_MODE = os.getenv("AGENTGOV_HOSTS_MODE", "mock")
|
|
83
|
+
HOSTS_MOCK_URL = os.getenv("AGENTGOV_HOSTS_MOCK_URL", "http://localhost:8400")
|
|
84
|
+
|
|
85
|
+
LITELLM_URL = os.getenv("LITELLM_URL", "") # real: http://litellm:4000
|
|
86
|
+
LITELLM_MASTER_KEY = os.getenv("LITELLM_MASTER_KEY", "sk-agentgov-local-master")
|
|
87
|
+
|
|
88
|
+
# AgentCore: the GATEWAY's credentials. Never the user's, and never a static key in real mode —
|
|
89
|
+
# app/hosts/agentcore.py::_gateway_credentials() resolves the ECS task role's temporary STS
|
|
90
|
+
# credentials from the ambient AWS chain instead (§5.7). These two are MOCK-ONLY fallbacks
|
|
91
|
+
# (local/hosts_mock doesn't touch real AWS, so fixed demo values are fine there).
|
|
92
|
+
AGENTCORE_URL = os.getenv("AGENTCORE_URL", "")
|
|
93
|
+
AGENTCORE_REGION = os.getenv("AGENTCORE_REGION", "us-east-1")
|
|
94
|
+
AGENTCORE_ACCESS_KEY_ID = os.getenv("AGENTCORE_ACCESS_KEY_ID", "AKIAAGENTGOVDEMO0000")
|
|
95
|
+
AGENTCORE_SECRET_ACCESS_KEY = os.getenv("AGENTCORE_SECRET_ACCESS_KEY", "agentgov-local-dev-agentcore-secret")
|
|
96
|
+
|
|
97
|
+
DATABRICKS_URL = os.getenv("DATABRICKS_URL", "") # real: https://<workspace>.cloud.databricks.com
|
|
98
|
+
DATABRICKS_UC_SECURABLE = os.getenv("DATABRICKS_UC_SECURABLE", "iops.manufacturing")
|
|
99
|
+
# Privileged admin bearer (PAT or OAuth token scoped to SCIM + Unity Catalog + Apps) the
|
|
100
|
+
# GATEWAY uses for JIT identity: SCIM CREATE USER + UC GRANT/REVOKE. Never a user token.
|
|
101
|
+
# Empty in mock (the mock host doesn't check it).
|
|
102
|
+
DATABRICKS_TOKEN = os.getenv("DATABRICKS_TOKEN", "")
|
|
103
|
+
|
|
104
|
+
SNOWFLAKE_URL = os.getenv("SNOWFLAKE_URL", "") # real: https://<account>.snowflakecomputing.com
|
|
105
|
+
SNOWFLAKE_ROLE_SCOPE = os.getenv("SNOWFLAKE_ROLE_SCOPE", "session:role:GD_CLINICAL_READER")
|
|
106
|
+
# Privileged key-pair for JIT CREATE USER / GRANT / REVOKE (the gateway's admin identity).
|
|
107
|
+
# Store the PRIVATE KEY, not a pre-minted JWT — key-pair JWTs expire in <=1h, so the adapter
|
|
108
|
+
# signs a fresh one per call (app/hosts/snowflake.py::_admin_jwt). Never a user token.
|
|
109
|
+
# Secret: agentgov/prod/snowflake-admin {account,user,private_key,private_key_passphrase}.
|
|
110
|
+
SNOWFLAKE_ADMIN_ACCOUNT = os.getenv("SNOWFLAKE_ADMIN_ACCOUNT", "") # e.g. AULMROR-NLC43968
|
|
111
|
+
SNOWFLAKE_ADMIN_USER = os.getenv("SNOWFLAKE_ADMIN_USER", "") # SECURITYADMIN-capable user
|
|
112
|
+
SNOWFLAKE_ADMIN_ROLE = os.getenv("SNOWFLAKE_ADMIN_ROLE", "SECURITYADMIN")
|
|
113
|
+
SNOWFLAKE_ADMIN_PRIVATE_KEY = os.getenv("SNOWFLAKE_ADMIN_PRIVATE_KEY", "") # PKCS8 PEM
|
|
114
|
+
SNOWFLAKE_ADMIN_PRIVATE_KEY_PASSPHRASE = os.getenv("SNOWFLAKE_ADMIN_PRIVATE_KEY_PASSPHRASE", "")
|
|
115
|
+
|
|
116
|
+
# HostDiscoverySync (Temporal Schedule). Shrunk by the local harness so a scheduled sync
|
|
117
|
+
# is observable during a test run; the demo/prod default is hourly.
|
|
118
|
+
HOST_SYNC_INTERVAL_MINUTES = float(os.getenv("HOST_SYNC_INTERVAL_MINUTES", "60"))
|
|
119
|
+
PULL_HOSTS = [h.strip() for h in os.getenv(
|
|
120
|
+
"AGENTGOV_PULL_HOSTS", "bedrock-agentcore,databricks,snowflake").split(",") if h.strip()]
|
|
121
|
+
|
|
122
|
+
# Browser origins allowed to call the API (the local registry-ui page; the deployed UI is
|
|
123
|
+
# same-origin behind the ALB path prefix, so this stays a dev convenience).
|
|
124
|
+
CORS_ORIGINS = [o.strip() for o in os.getenv(
|
|
125
|
+
"CORS_ORIGINS", "http://localhost:8500,http://127.0.0.1:8500").split(",") if o.strip()]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
settings = Settings()
|