Access Governance
The GASP twin gives every agent the same view of what metrics exist and how they connect, which is the reason to build one. Agents act on behalf of users, though, so the same agent has to show a sales rep and a CFO different numbers. This page is GASP's opinion on how to scope permissions in an agentic SaaS system with the policy engines you already run. It covers access to metric values. How the standard itself is maintained and changed is in GOVERNANCE.md.
The Problem
If every agent reads the same twin and every user can invoke any agent, then by default every user has access to every metric. A Support Manager's retention agent could ask the Finance twin for Customer Acquisition Cost (CAC). A sales rep's pipeline agent could read the pipeline of colleagues in other territories. Without governance the twin becomes a way around the access controls your security team has already built.
Enterprise access control is an old problem with well-tested answers: role and attribute policies, row-level security and delegated tokens. GASP's job is to give those answers a shared vocabulary for metrics.
The Three-Layer Model
Access control in an agentic system happens at three layers. Each one is enforced in a different place, so keep them separate when you design permissions.
Model Layer
Always visible
Every user can see what metrics exist, their canonical definitions, their relationships and their required fields. Treat the model as documentation: teams who cannot see it build shadow systems with duplicate definitions. A Support Manager should know what CAC is and where it lives in the graph, even if they cannot see the current number.
Computation Layer
Permission-gated
Whether a user can compute the current value of a metric is decided here, in the warehouse, through row-level security, column masking or a query rewriter. A query runs with the user's identity and permissions, so the warehouse makes the call rather than the agent.
Agent Layer
Delegated identity
Agents always act on behalf of a user and inherit that user's scope. A retention agent invoked by a Finance Manager sees different numbers than the same agent invoked by a Support Manager. Elevated service identities (board prep, compliance audit) are explicit, audited exceptions.
What GASP Adds
Two metric-level classifications give downstream policy systems stable identifiers to reference. They are recommendations for your own policy systems to enforce.
Sensitivity Tier
Aligns with ISO 27001, OSSA data_classification and AWS Cedar common patterns.
See the glossary entry for the canonical definition.
| Tier | Description |
|---|---|
| public | Shareable externally with no restriction (rare for internal SaaS metrics) |
| internal | Shareable across the organization. Default for most operational metrics |
| confidential | Cross-functional but sensitive. Typically restricted to managers and above |
| restricted | Highly sensitive. Executive and finance-leadership only by default |
Access Scope
Describes whether a metric is naturally filterable per user context (the ReBAC/ABAC dimension). See the glossary entry for the canonical definition.
| Scope | Description |
|---|---|
| unscoped | Same value for everyone with access (e.g. company-wide ARR) |
| department-scoped | Filtered by the viewer's department context |
| owner-scoped | Visible only for records the viewer owns or manages (individual, team, territory, book or segment: interpretation lives in the adapter) |
Recommended Classifications
Default sensitivity and scope for the 69 metrics in the GASP relationship graph. The other 244 metrics carry no recommendation yet; classify them with the decision framework below. Use these as a starting point. Your organisation's risk profile may justify different choices.
| Metric | Sensitivity | Scope |
|---|---|---|
| Rule of 40 rule_of_40 | restricted | unscoped |
| Operating Margin operating_margin | restricted | unscoped |
| Free Cash Flow fcf | restricted | unscoped |
| Cash Burn Rate burn_rate | restricted | unscoped |
| Revenue per Employee revenue_per_employee | restricted | unscoped |
| Magic Number magic_number | restricted | unscoped |
| Employee Turnover Rate employee_turnover | restricted | unscoped |
| Employee NPS enps | restricted | unscoped |
| Pipeline Value pipeline_value | confidential | owner-scoped |
| Pipeline Coverage pipeline_coverage | confidential | owner-scoped |
| ACV acv | confidential | owner-scoped |
| Quota Attainment quota_attainment | confidential | owner-scoped |
| Win Rate win_rate | confidential | department-scoped |
| Forecast Accuracy forecast_accuracy | confidential | department-scoped |
| Cost per Lead cpl | confidential | department-scoped |
| Marketing Sourced Pipeline marketing_sourced_pipeline | confidential | department-scoped |
| Expansion Revenue Rate expansion_rate | confidential | department-scoped |
| Renewal Rate renewal_rate | confidential | department-scoped |
| Partner-Sourced Revenue partner_sourced_revenue | confidential | department-scoped |
| ARR arr | confidential | unscoped |
| MRR mrr | confidential | unscoped |
| ARPA arpa | confidential | unscoped |
| MRR Growth Rate mrr_growth_rate | confidential | unscoped |
| New MRR new_mrr | confidential | unscoped |
| Expansion MRR expansion_mrr | confidential | unscoped |
| Churned MRR churned_mrr | confidential | unscoped |
| Reactivation MRR reactivation_mrr | confidential | unscoped |
| New Customers new_customers | confidential | unscoped |
| Churned Customers churned_customers | confidential | unscoped |
| Contraction MRR contraction_mrr | confidential | unscoped |
| Net New MRR net_new_mrr | confidential | unscoped |
| NRR nrr | confidential | unscoped |
| GRR grr | confidential | unscoped |
| Gross Margin gross_margin | confidential | unscoped |
| Logo Churn Rate logo_churn_rate | confidential | unscoped |
| Revenue Churn Rate revenue_churn_rate | confidential | unscoped |
| CAC cac | confidential | unscoped |
| LTV ltv | confidential | unscoped |
| LTV:CAC Ratio ltv_cac_ratio | confidential | unscoped |
| CAC Payback Period cac_payback | confidential | unscoped |
| Marketing CAC marketing_cac | confidential | unscoped |
| Days Sales Outstanding dso | confidential | unscoped |
| Consumption Revenue consumption_revenue | confidential | unscoped |
| Usage Run-Rate usage_run_rate | confidential | unscoped |
| Prepaid Credit Balance prepaid_credit_balance | confidential | unscoped |
| ARR Including Usage Run-Rate arr_including_usage_run_rate | confidential | unscoped |
| Customer Health Score health_score | internal | owner-scoped |
| At-Risk Customer Rate at_risk_rate | internal | owner-scoped |
| Sales Cycle Length sales_cycle | internal | department-scoped |
| QBR Completion Rate qbr_completion | internal | department-scoped |
| CSAT csat | internal | department-scoped |
| First Response Time frt | internal | department-scoped |
| Ticket Volume ticket_volume | internal | department-scoped |
| Time to First Value ttfv | internal | department-scoped |
| Onboarding Completion Rate onboarding_completion | internal | department-scoped |
| Product Qualified Accounts pqas | internal | department-scoped |
| Deployment Frequency deployment_frequency | internal | department-scoped |
| MTTR mttr | internal | department-scoped |
| Total Customers total_customers | internal | unscoped |
| NPS nps | internal | unscoped |
| MQLs mqls | internal | unscoped |
| SQLs sqls | internal | unscoped |
| Lead Velocity Rate lvr | internal | unscoped |
| Self-Service Rate self_service_rate | internal | unscoped |
| Activation Rate activation_rate | internal | unscoped |
| Trial-to-Paid Conversion Rate trial_to_paid_conversion_rate | internal | unscoped |
| DAU/MAU Ratio dau_mau | internal | unscoped |
| Feature Adoption Rate feature_adoption | internal | unscoped |
| Uptime uptime | internal | unscoped |
A Decision Framework
When classifying a new metric, work through three questions in order:
- Who needs it by default?
If the answer is "the whole company," the tier is
internal. If the answer is "managers and above," it isconfidential. If it is "finance leadership and the CEO," it isrestricted. - Is it filterable?
If the same metric makes sense with per-user context (my pipeline, my accounts, my team's tickets),
give it a scope other than
unscoped. A sales rep should see pipeline but only their own. - Does computing it leak something the value alone does not? Aggregate marketing spend is less sensitive than per-campaign spend. Aggregate CAC is less sensitive than CAC broken down by channel. Publish the aggregate and restrict the breakdowns that expose competitive detail. Breakdowns are declared in the canonical formulas (CAC by cost centre, Win Rate by channel). The recommended tier and scope apply to the metric as a whole, so your policy can set a stricter tier on a breakdown.
Integration Patterns
GASP tags are metadata, read by the policy systems that do the enforcing. Four patterns cover most implementations.
Cedar Policies
Cedar is an open-source policy language used by AWS Verified Permissions and referenced in the OSSA governance schema. Express GASP classifications as resource attributes:
// Principal: User with department and clearance attributes
// Resource: GASP metric with sensitivityTier and accessScope
permit (
principal in Department::"Finance",
action == Action::"query",
resource
)
when {
resource has sensitivityTier &&
resource.sensitivityTier in ["public", "internal", "confidential", "restricted"]
};
// Department managers see internal + confidential for their dept
permit (
principal,
action == Action::"query",
resource
)
when {
principal has role && principal.role == "manager" &&
resource.sensitivityTier in ["internal", "confidential"] &&
(resource.accessScope == "unscoped" ||
resource.department == principal.department)
};
// Everyone sees internal + unscoped
permit (
principal,
action == Action::"query",
resource
)
when {
resource.sensitivityTier == "internal" &&
resource.accessScope == "unscoped"
};
// Restricted tier: explicit allowlist only
forbid (
principal,
action == Action::"query",
resource
)
unless {
resource.sensitivityTier != "restricted" ||
principal in Group::"FinanceLeadership"
}; Open Policy Agent (Rego)
OPA is a CNCF graduated policy engine and a common choice for policy-as-code in large companies. The same GASP classifications expressed in Rego:
package gasp.authz
import rego.v1
default allow := false
# Everyone sees internal + unscoped metrics
allow if {
input.resource.sensitivityTier == "internal"
input.resource.accessScope == "unscoped"
}
# Department managers see confidential within their department
allow if {
input.principal.role == "manager"
input.resource.sensitivityTier in {"internal", "confidential"}
manager_can_see_scope
}
manager_can_see_scope if input.resource.accessScope == "unscoped"
manager_can_see_scope if {
input.resource.accessScope == "department-scoped"
input.resource.department == input.principal.department
}
manager_can_see_scope if {
input.resource.accessScope == "owner-scoped"
input.resource.owner == input.principal.user_id
}
# Restricted requires explicit finance leadership membership
allow if {
input.resource.sensitivityTier == "restricted"
"finance_leadership" in input.principal.groups
} Warehouse Row-Level Security
Row-level security is where enforcement reaches the data. Snowflake, BigQuery and Postgres all support it, filtering the result set by the user's identity. You can generate the policies from GASP classifications.
-- Snowflake: owner-scoped pipeline visibility
-- "Owner" can be an individual rep, a territory, a book or a segment.
-- Interpretation lives in the ownership_assignments table, not in GASP.
CREATE ROW ACCESS POLICY pipeline_owner_policy AS (owner_id VARCHAR)
RETURNS BOOLEAN ->
CASE
WHEN CURRENT_ROLE() IN ('SYSADMIN', 'FINANCE_LEADERSHIP') THEN TRUE
WHEN CURRENT_USER() IN (
SELECT user_id FROM governance.ownership_assignments
WHERE assigned_owner_id = owner_id
) THEN TRUE
ELSE FALSE
END;
ALTER TABLE fct_opportunities
ADD ROW ACCESS POLICY pipeline_owner_policy ON (owner_id);
-- BigQuery: restricted-tier column masking via data policy
CREATE OR REPLACE TABLE company_metrics OPTIONS (
policy_tags_on_columns = [
('burn_rate', 'projects/acme/locations/us/taxonomies/gasp/policyTags/restricted'),
('operating_margin', 'projects/acme/locations/us/taxonomies/gasp/policyTags/restricted')
]
); MCP OAuth Scopes
An agent always acts on behalf of a user and inherits that user's scope through a delegated identity. The 23 GASP MCP tools return model-layer information, SQL text and Ossie documents; the only values they compute come from GASP's synthetic dataset, so one read scope covers them all. Gate the tool that runs SQL against your warehouse instead: map GASP sensitivity tiers to OAuth scopes on that executor and let warehouse row-level security apply the access scope.
// OAuth scopes: one read scope for the GASP MCP tools.
// run_query is your SQL executor, the only tool that returns values;
// each metrics:query scope admits metrics of that GASP sensitivity tier.
{
"scopes": {
"gasp:read": ["lookup_metric", "lookup_definition", "list_metrics",
"get_formula", "get_relationships", "get_data_requirements",
"search", "generate_query", "validate_mapping", "validate_adapter",
"compile_model", "score_drift", "profile_schema", "propose_mapping", "record_decision", "derive_cel", "generate_checks", "plan_fixes", "infer_drivers", "explain_path", "coverage_gap"],
"metrics:query:public": ["run_query"],
"metrics:query:internal": ["run_query"],
"metrics:query:confidential": ["run_query"],
"metrics:query:restricted": ["run_query"]
},
"role_scopes": {
"ic": ["gasp:read", "metrics:query:public", "metrics:query:internal"],
"manager": ["gasp:read", "metrics:query:public", "metrics:query:internal",
"metrics:query:confidential"],
"exec": ["gasp:read", "metrics:query:public", "metrics:query:internal",
"metrics:query:confidential", "metrics:query:restricted"]
}
} Agent Integration Patterns
The protocols covered above handle single-user data access. Agentic systems add a second identity layer: agents act on behalf of users but have their own permissions too. OSSA (agent contracts) and A2A (inter-agent communication) meet GASP at that second layer.
The Three-Ceiling Model
A common mistake in agentic systems is letting agents run with service accounts that have more access than the user who invoked them. Effective access is the intersection of three ceilings:
- Agent clearance comes from the OSSA manifest (
authorization.clearance_level) - User clearance comes from your identity provider (Okta, Google Workspace, Azure AD)
- Metric sensitivity comes from GASP (
accessControl.classifications)
The warehouse enforces the intersection through row-level security and column masking. The agent and the application describe the constraints and the warehouse computes the answer.
Token Propagation (A2A)
When a coordinator agent calls a specialist via A2A, the user's token must propagate through the call chain. If the coordinator uses its own service token, privilege escalates silently. The pattern is OAuth 2 Token Exchange (RFC 8693):
// Coordinator receives a request with the user's token
// It does NOT use its own service token to call specialists
// 1. Coordinator exchanges the user token for a delegated token
// scoped to the specialist audience
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=https://agents.acme.com/retention-specialist
&actor_token=
&actor_token_type=urn:ietf:params:oauth:token-type:access_token
&scope=gasp:read metrics:query:confidential
// 2. Coordinator calls the specialist with the delegated token
POST https://agents.acme.com/retention-specialist
Authorization: Bearer
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "req-coord-001",
"method": "SendMessage",
"params": { "message": { "messageId": "msg-coord-002", "role": "ROLE_USER", "parts": [...] } }
}
// The specialist now executes warehouse queries with the ORIGINAL user's
// identity. If the user cannot see restricted metrics, neither can the
// specialist acting on their behalf. No privilege escalation via chaining. Human Review for Restricted Access (OSSA)
OSSA's governance.authorization marks which tools need a human approval and which policies the agent must follow.
Together with a policy that reads the GASP tier tag, this is a cheap safety valve for the restricted tier:
# In the agent's .ossa.yaml manifest
apiVersion: ossa/v0.5.6
kind: Agent
metadata:
name: gasp-retention-analyst
governance:
authorization:
clearance_level: 3 # handles up to confidential on its own
tool_permissions:
- tool: lookup_metric # reads the twin: never gated
risk_level: low
requires_approval: false
- tool: run_query # your SQL executor
risk_level: high
requires_approval: true # the runtime pauses for a reviewer
policy_references:
- gasp.restricted_metrics_require_review # approves unless a metric is restricted
cognition:
governance:
audit_required: true
alignmentGate:
enabled: true
arbiter: human
timeout: 4h
When the retention agent, asked to explain a Net Revenue Retention (NRR) drop, requests Burn Rate (a restricted metric) for context, the OSSA runtime pauses the query
and asks a reviewer in Finance Leadership. The user sees "waiting for review" rather than getting the number silently.
The manifest says only that the query tool is approval-gated. The referenced policy decides when, and all it has to compare is the GASP tier tag on the metrics in the request, so a query over public and internal metrics goes straight through.
OSSA's own human_review_triggers fire on reasoning signals (low confidence, a contradiction, branch depth), so the tool gate lives in governance.authorization instead.
The Audit Tuple Requirement
Traditional warehouse audit logs capture one identity: whoever ran the query. In agentic systems you need three: the user who asked, the agent that ran and the session that connected them. With only the first, the day an agent does something unexpected you can see the query but cannot tell who asked for it.
-- Extend warehouse queries to carry the full identity tuple
-- Snowflake query tag example
ALTER SESSION SET QUERY_TAG = OBJECT_CONSTRUCT(
'user_id', 'user-sarah-cfo',
'agent_id', '[email protected]',
'session_id', 'a2a-ctx-abc-123',
'parent_task', 'task-coord-001',
'gasp_metric', 'nrr',
'tier', 'confidential'
)::STRING;
-- Then correlate across four log sources:
-- 1. Identity provider (user authentication)
-- 2. A2A message log (agent invocations)
-- 3. MCP server log (tool calls)
-- 4. Warehouse query log (actual data access)
-- Joined on session_id, you get the complete chain from
-- "user Sarah asked a question" to "specifically these rows were read".
OSSA and A2A have no standard audit format yet. GASP recommends propagating the tuple on every call, joining the four logs on session_id
and keeping them as long as your compliance framework requires (typically 90 to 365 days).
Who Does What
| Concern | Owner | Where it is configured |
|---|---|---|
| Metric sensitivity classification | GASP | accessControl.classifications in ontology |
| Agent clearance ceiling | OSSA | authorization.clearance_level in agent manifest |
| User identity and role | Identity provider | Okta, Google Workspace, Azure AD |
| Token propagation between agents | A2A | OAuth 2 Token Exchange (RFC 8693) |
| Human-in-the-loop gates | OSSA | governance.authorization.tool_permissions with requires_approval |
| Policy evaluation | Cedar / OPA | Policy-as-code referencing GASP tier tags |
| Row-level and column enforcement | Warehouse | Snowflake RLS, BigQuery policy tags, Postgres RLS |
| Audit logging | Identity + A2A + MCP + Warehouse | Propagate (user, agent, session, task) tuple through all four |
Common Dilemmas
The Support Manager and CAC
A Support Manager wants to understand CAC to argue that reducing support friction lowers churn and improves unit economics.
GASP tags CAC as confidential and unscoped. Should they see it?
Recommended resolution: Yes, in aggregate. A Support Manager should see company-level CAC as part of operating context. They should not see CAC broken down by channel, by quarter or by campaign, because those cuts show where and how hard the company is spending to win customers.
Board Prep Agent and Restricted Metrics
The board deck needs burn rate, runway and operating margin. Burn rate and operating margin are restricted. Runway is derived from the cash balance and burn rate.
Should an automated agent be able to pull these?
Recommended resolution: Yes, through an explicit service identity with elevated scope. The agent runs as a governance-approved service account with its own audit trail, so when the CFO invokes it, its permissions come from that account rather than from the CFO. Every invocation is logged, the same pattern as a CI pipeline with elevated deployment permissions.
Cross-Functional Dashboards
A company-wide health dashboard shows NRR, Gross Margin and Employee Net Promoter Score (eNPS). NRR and Gross Margin are confidential and eNPS is restricted, so the panels have two different audiences.
How should this be governed?
Recommended resolution: The dashboard renders what each viewer is allowed to see. A department manager sees NRR and Gross Margin but the eNPS panel is masked or hidden. Everyone runs the same dashboard code and the warehouse filters per viewer through the row-level policies above, so the dashboard needs no permission logic of its own.
What about agents that call other agents?
A coordinator agent invokes a Finance specialist agent to compute Rule of 40. The coordinator was invoked by a Sales Manager. Whose permissions apply?
Recommended resolution: Always the originating human identity. Protocols like A2A propagate the caller token. The Finance specialist executes the query with the Sales Manager's credentials, which hit the warehouse's row-level policies. If the Sales Manager cannot see restricted metrics, neither can any agent acting on their behalf.
What GASP Does Not Do
GASP is a standard, not a runtime. Specifically:
- GASP does not enforce permissions. Enforcement belongs in Cedar, OPA, your identity provider or your warehouse.
- GASP does not define user roles. Your organisation designs its own role hierarchy and GASP gives those roles a vocabulary to reference.
- GASP does not audit access. Audit logging happens where queries run (the warehouse) and where tokens are issued (the identity layer).
- GASP does not replace your data governance program. It complements SOC2, ISO 27001 and existing compliance frameworks.
GASP adds the shared vocabulary. Once every metric has a stable ID, a recommended tier and a recommended scope, policies move between tools: a Cedar policy written against GASP metric IDs works the same on Snowflake or BigQuery, with Okta or Google Workspace as the identity provider.