ADR-0014 — Modular resource scope : schema / database / account
Date : 2026-05-31 Status : Accepted
Context
The provider deploys two distinct categories of objects:
-
CI/CD deployments (via CLI on Git runners) — deploys the full object graph: schemas, databases, account-level resources (warehouses, roles, integrations...).
-
Runtime provisioning (via Python API inside
PINKY.BILLING.PROVISION_CLIENT()SP) — deploys a client schema dynamically at webhook time (new subscriber). Runs inside Snowflake viaEXECUTE AS OWNER, called fromARTIFACT_REPOSITORY.
These two contexts have different requirements: - The SP needs only schema-scoped objects — importing the full provider is unnecessary and unsafe. - The CLI needs all resource types. - The Native App (PINKY on Marketplace) must import only what it needs for provisioning.
Decision
Split provider resources into three scope modules. The cli/ module is never imported inside a SP.
pinky_provider/
├── core/ ← diff engine, manifest parser, session (shared)
├── resources/
│ ├── schema/ ← objects that live in a schema (see list below)
│ ├── database/ ← objects that live in a database
│ └── account/ ← objects that live at account level
├── dag/ ← meta-resource DAG (generates tasks — schema scope)
└── cli/ ← click + rich (never imported in a SP or Native App)
Schema scope — pinky_provider.resources.schema
Objects that can exist inside a client schema. Used by PROVISION_CLIENT().
| Resource | Note |
|---|---|
table |
|
iceberg_table |
|
storage_lifecycle_policy |
|
tag |
|
view |
|
dynamic_table |
|
procedure |
|
user_defined_function |
|
task / task.dag |
|
stage |
|
stream |
|
event_table |
|
alert |
|
streamlit |
|
secret |
client-delegated credentials — e.g. Pronote API key (Pluto School), INPI token (Pluto Forms). Lives in client schema → deleted at DROP SCHEMA (RGPD by design). |
secret is schema-scoped here because each client owns their own credentials. These are not shared
infrastructure secrets (those live in PINKY.SECURITY) — they are per-client delegated access tokens.
Database scope — pinky_provider.resources.database
| Resource | Note |
|---|---|
database_role |
|
grant IN DATABASE |
|
schema |
CREATE SCHEMA at provisioning time |
Account scope — pinky_provider.resources.account
| Resource | Note |
|---|---|
database |
|
warehouse |
|
role |
|
user |
|
network_policy |
|
network_rule |
|
secret (shared) |
PINKY.SECURITY secrets — not client-specific |
api_integration |
|
external_access_integration |
SQL fallback |
external_volume |
|
compute_pool |
|
notification_integration |
|
tag |
Usage
PROVISION_CLIENT() SP — schema scope only
# ARTIFACT_REPOSITORY_PACKAGES = ('pinky-provider[schema]')
from pinky_provider.resources.schema import SchemaProvider
def provision_client(session, team_id, product_db, client_id, ...):
provider = SchemaProvider(session)
provider.apply(
source = f"{product_db}.PUBLIC.GIT_REPO:/manifest_client/",
target = f"{product_db}.{client_id}",
vars = {"client_id": client_id}
)
# Can create: table, view, procedure, stage, secret (client creds)...
# Cannot create: warehouse, role, user, EAI — physically not in scope
CLI — full scope
# pip install pinky-provider (full)
from pinky_provider import Provider
provider = Provider(session)
provider.apply(source="manifest.yml", target="MY_DB.PUBLIC")
# All resource types available
Consequences
Positive:
- PROVISION_CLIENT() SP imports a lighter wheel → faster cold start via ARTIFACT_REPOSITORY
- Physical security boundary: even if the SP is compromised, it cannot create account-level objects
- DROP SCHEMA {client_id} cascades client secrets (Pronote, INPI...) → RGPD by design
- Clean separation of deployment contexts (CI/CD vs runtime)
- Native App (PINKY on Marketplace) imports only schema + database modules — not account
Negative:
- Two import paths to maintain (SchemaProvider vs Provider)
- manifest_client/ must declare only schema-scoped resources — validation needed at plan time
Rejected alternatives
Single monolithic provider — importing the full provider in PROVISION_CLIENT() works but:
- Larger wheel in ARTIFACT_REPOSITORY (unnecessary cold start cost)
- No physical enforcement of schema-only scope in the SP
- Violates principle of least privilege at the library level