@domesystems/templates 0.1.1 → 0.2.0
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.
- package/README.md +41 -150
- package/package.json +5 -5
- package/src/cli.mjs +100 -26
- package/src/runtime.mjs +2 -2
- package/src/template-files.mjs +72 -0
- package/ai-agent-for-documentation/.env.example +0 -12
- package/ai-agent-for-documentation/app/agent.yaml +0 -31
- package/ai-agent-for-documentation/app/help.md +0 -19
- package/ai-agent-for-documentation/app/instructions.md +0 -21
- package/ai-agent-for-documentation/docker-compose.yml +0 -18
- package/ai-agent-for-documentation/dome.tf +0 -146
- package/ai-agent-for-documentation/rules/answerer.cedar +0 -35
- package/ai-agent-for-documentation/template.yaml +0 -131
- package/ai-agent-for-documentation/verification.json +0 -7
- package/ai-agent-for-notion/.env.example +0 -19
- package/ai-agent-for-notion/app/agent.yaml +0 -32
- package/ai-agent-for-notion/app/help.md +0 -14
- package/ai-agent-for-notion/app/instructions.md +0 -16
- package/ai-agent-for-notion/docker-compose.yml +0 -18
- package/ai-agent-for-notion/dome.tf +0 -163
- package/ai-agent-for-notion/guards/redact-ssn-from-pages.json +0 -22
- package/ai-agent-for-notion/rules/answerer.cedar +0 -29
- package/ai-agent-for-notion/template.yaml +0 -143
- package/ai-agent-for-notion/verification.json +0 -7
- package/ai-agent-for-release-notes/.env.example +0 -15
- package/ai-agent-for-release-notes/app/agent.yaml +0 -36
- package/ai-agent-for-release-notes/app/help.md +0 -20
- package/ai-agent-for-release-notes/app/instructions.md +0 -19
- package/ai-agent-for-release-notes/docker-compose.yml +0 -18
- package/ai-agent-for-release-notes/dome.tf +0 -165
- package/ai-agent-for-release-notes/guards/block-leaked-keys.json +0 -38
- package/ai-agent-for-release-notes/rules/release-notes.cedar +0 -49
- package/ai-agent-for-release-notes/template.yaml +0 -156
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Without DOME_TOKEN this starts in offline mode: the chrome works and the
|
|
3
|
-
# allow-list is still enforced, but tools and model are simulated.
|
|
4
|
-
services:
|
|
5
|
-
app:
|
|
6
|
-
# Built and published by dome-systems/template-runtime.
|
|
7
|
-
image: ghcr.io/dome-systems/runtime-py-chat:dev
|
|
8
|
-
env_file: .env
|
|
9
|
-
volumes:
|
|
10
|
-
- ./app:/config:ro
|
|
11
|
-
ports:
|
|
12
|
-
- "3000:3000"
|
|
13
|
-
healthcheck:
|
|
14
|
-
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:3000/health', timeout=2)"]
|
|
15
|
-
interval: 30s
|
|
16
|
-
timeout: 3s
|
|
17
|
-
start_period: 10s
|
|
18
|
-
retries: 3
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
terraform {
|
|
2
|
-
required_providers {
|
|
3
|
-
dome = { source = "dome-systems/dome", version = ">= 0.1.0" }
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
variable "workspace_id" { type = string }
|
|
8
|
-
variable "anthropic_api_key" {
|
|
9
|
-
type = string
|
|
10
|
-
sensitive = true
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
resource "dome_gateway" "docs" {
|
|
14
|
-
workspace_id = var.workspace_id
|
|
15
|
-
name = "docs"
|
|
16
|
-
description = "Read surface over the public Dome Platform documentation"
|
|
17
|
-
is_default = true
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
resource "dome_mcp_connection" "domedocs" {
|
|
21
|
-
workspace_id = var.workspace_id
|
|
22
|
-
name = "domedocs"
|
|
23
|
-
protocol = "streamable-http"
|
|
24
|
-
url = "https://docs.domesystems.ai/mcp"
|
|
25
|
-
auth_method = "none"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
resource "dome_llm_connection" "haiku" {
|
|
29
|
-
workspace_id = var.workspace_id
|
|
30
|
-
name = "haiku"
|
|
31
|
-
provider_id = "anthropic"
|
|
32
|
-
provider_config = jsonencode({ model = "claude-haiku-4-5-20251001" })
|
|
33
|
-
auth_method = "api-key"
|
|
34
|
-
credential_type = "shared"
|
|
35
|
-
managed_header_name = "x-api-key"
|
|
36
|
-
secret_values = { "x-api-key" = var.anthropic_api_key }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
resource "dome_llm_pool" "docs" {
|
|
40
|
-
workspace_id = var.workspace_id
|
|
41
|
-
name = "docs-pool"
|
|
42
|
-
is_default = true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
resource "dome_llm_pool_member" "haiku" {
|
|
46
|
-
workspace_id = var.workspace_id
|
|
47
|
-
pool = dome_llm_pool.docs.name
|
|
48
|
-
llm_connection = dome_llm_connection.haiku.name
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
resource "dome_gateway_llm_pool" "docs" {
|
|
52
|
-
workspace_id = var.workspace_id
|
|
53
|
-
gateway = dome_gateway.docs.name
|
|
54
|
-
llm_pool = dome_llm_pool.docs.name
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
resource "dome_gateway_tool" "search" {
|
|
58
|
-
workspace_id = var.workspace_id
|
|
59
|
-
gateway = dome_gateway.docs.name
|
|
60
|
-
mcp_connection = dome_mcp_connection.domedocs.name
|
|
61
|
-
tool = "search"
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
resource "dome_gateway_tool" "get_page" {
|
|
65
|
-
workspace_id = var.workspace_id
|
|
66
|
-
gateway = dome_gateway.docs.name
|
|
67
|
-
mcp_connection = dome_mcp_connection.domedocs.name
|
|
68
|
-
tool = "get_page"
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
resource "dome_gateway_tool" "list_pages" {
|
|
72
|
-
workspace_id = var.workspace_id
|
|
73
|
-
gateway = dome_gateway.docs.name
|
|
74
|
-
mcp_connection = dome_mcp_connection.domedocs.name
|
|
75
|
-
tool = "list_pages"
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
resource "dome_agent" "docs_answerer" {
|
|
79
|
-
workspace_id = var.workspace_id
|
|
80
|
-
name = "docs-answerer"
|
|
81
|
-
allowed_gateways = [dome_gateway.docs.name]
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
resource "dome_agent_rules_bundle" "docs_answerer" {
|
|
85
|
-
workspace_id = var.workspace_id
|
|
86
|
-
agent = dome_agent.docs_answerer.name
|
|
87
|
-
files = {
|
|
88
|
-
"answerer.cedar" = <<-EOT
|
|
89
|
-
permit ( principal, action == Dome::Action::"mcp:discover", resource );
|
|
90
|
-
permit ( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"domedocs/search" );
|
|
91
|
-
permit ( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"domedocs/get_page" );
|
|
92
|
-
forbid ( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"domedocs/list_pages" );
|
|
93
|
-
EOT
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
resource "dome_filter" "keep_ssn_out_of_prompts" {
|
|
98
|
-
workspace_id = var.workspace_id
|
|
99
|
-
name = "keep-ssn-out-of-prompts"
|
|
100
|
-
description = "Redact US SSN patterns out of anything sent to the provider"
|
|
101
|
-
config = jsonencode({
|
|
102
|
-
text = { components = [{ action = "FILTER_ACTION_REDACT", matchers = [{ ssn = {} }] }] }
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
resource "dome_llm_connection_filters" "haiku_requests" {
|
|
107
|
-
workspace_id = var.workspace_id
|
|
108
|
-
llm_connection = dome_llm_connection.haiku.name
|
|
109
|
-
direction = "request"
|
|
110
|
-
filters = [dome_filter.keep_ssn_out_of_prompts.name]
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
resource "dome_quota" "agent_daily_spend" {
|
|
114
|
-
workspace_id = var.workspace_id
|
|
115
|
-
dimension = "llm"
|
|
116
|
-
unit = "dome_usd"
|
|
117
|
-
subject_type = "agent"
|
|
118
|
-
subject = dome_agent.docs_answerer.name
|
|
119
|
-
scope = "total"
|
|
120
|
-
window = "daily"
|
|
121
|
-
limit_amount = 5000000
|
|
122
|
-
name = "docs-answerer daily spend"
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
resource "dome_quota" "search_calls" {
|
|
126
|
-
workspace_id = var.workspace_id
|
|
127
|
-
dimension = "tool"
|
|
128
|
-
unit = "calls"
|
|
129
|
-
subject_type = "tool_connection"
|
|
130
|
-
subject = dome_mcp_connection.domedocs.name
|
|
131
|
-
scope = "total"
|
|
132
|
-
window = "daily"
|
|
133
|
-
limit_amount = 500
|
|
134
|
-
name = "domedocs search calls"
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
resource "dome_agent_key" "runtime" {
|
|
138
|
-
workspace_id = var.workspace_id
|
|
139
|
-
agent = dome_agent.docs_answerer.name
|
|
140
|
-
name = "runtime"
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
output "docs_answerer_token" {
|
|
144
|
-
value = dome_agent_key.runtime.token
|
|
145
|
-
sensitive = true
|
|
146
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// answer-from-dome-docs — authorization rules
|
|
2
|
-
//
|
|
3
|
-
// The documentation MCP server is read-only upstream: there is no write tool to
|
|
4
|
-
// forbid here, so the boundary this template demonstrates is the allow-list
|
|
5
|
-
// itself. `search` and `get_page` are what answering a question needs.
|
|
6
|
-
// `list_pages` returns the entire documentation index — the call an agent makes
|
|
7
|
-
// to enumerate a corpus rather than to answer about it — so it is on the
|
|
8
|
-
// gateway, visible to the model, and denied.
|
|
9
|
-
//
|
|
10
|
-
// The write-boundary version of this story is
|
|
11
|
-
// ai-agent-for-notion, where a Notion page-creation tool is
|
|
12
|
-
// reachable and refused.
|
|
13
|
-
|
|
14
|
-
permit ( principal, action == Dome::Action::"mcp:discover", resource );
|
|
15
|
-
|
|
16
|
-
permit (
|
|
17
|
-
principal,
|
|
18
|
-
action == Dome::Action::"mcp:call",
|
|
19
|
-
resource == Dome::MCPTool::"domedocs/search"
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
permit (
|
|
23
|
-
principal,
|
|
24
|
-
action == Dome::Action::"mcp:call",
|
|
25
|
-
resource == Dome::MCPTool::"domedocs/get_page"
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
// Registering an agent against a gateway auto-generates an Agent Form policy
|
|
29
|
-
// permitting any tool whose `gateways` attribute contains that gateway. A
|
|
30
|
-
// permit cannot narrow that; only a forbid can. Cedar forbid always wins.
|
|
31
|
-
forbid (
|
|
32
|
-
principal,
|
|
33
|
-
action == Dome::Action::"mcp:call",
|
|
34
|
-
resource == Dome::MCPTool::"domedocs/list_pages"
|
|
35
|
-
);
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
apiVersion: templates.domesystems.ai/v1
|
|
2
|
-
kind: Template
|
|
3
|
-
|
|
4
|
-
metadata:
|
|
5
|
-
name: answer-from-dome-docs
|
|
6
|
-
slug: ai-agent-for-documentation
|
|
7
|
-
title: Ask questions about product documentation
|
|
8
|
-
target_query: ai chatbot for documentation
|
|
9
|
-
summary: >-
|
|
10
|
-
A chat agent that answers questions from the Dome Platform documentation.
|
|
11
|
-
Nothing to set up upstream — the documentation MCP server takes no
|
|
12
|
-
credential, so this reaches a real system on the first run. The connection
|
|
13
|
-
carries three tools. Two answer questions. The third returns the whole
|
|
14
|
-
documentation index, and the agent will reach for it — Dome refuses, and
|
|
15
|
-
names the rule that refused it.
|
|
16
|
-
domain: productivity
|
|
17
|
-
shape: interactive-assistant
|
|
18
|
-
ui: chat
|
|
19
|
-
business_pattern: consumes
|
|
20
|
-
governance_interest: [read-only, tool-scoped-access]
|
|
21
|
-
|
|
22
|
-
# Library-only topology and documentation data. `dome.tf` is the deployable source.
|
|
23
|
-
catalog:
|
|
24
|
-
gateway:
|
|
25
|
-
name: docs
|
|
26
|
-
description: Read surface over the public Dome Platform documentation
|
|
27
|
-
default: true
|
|
28
|
-
|
|
29
|
-
connections:
|
|
30
|
-
- name: domedocs
|
|
31
|
-
protocol: streamable-http
|
|
32
|
-
url: https://docs.domesystems.ai/mcp
|
|
33
|
-
# The whole point of this template. A public MCP server, no token, no
|
|
34
|
-
# consent screen, no account to create upstream.
|
|
35
|
-
auth:
|
|
36
|
-
method: none
|
|
37
|
-
permit:
|
|
38
|
-
- search
|
|
39
|
-
- get_page
|
|
40
|
-
# On the gateway on purpose, so the model can see it and try. Cedar
|
|
41
|
-
# forbids it, and a forbid beats the auto-generated Agent Form permit.
|
|
42
|
-
# Without this there is no denial to observe.
|
|
43
|
-
forbid:
|
|
44
|
-
- list_pages
|
|
45
|
-
|
|
46
|
-
model:
|
|
47
|
-
name: haiku
|
|
48
|
-
provider: anthropic
|
|
49
|
-
model_id: claude-haiku-4-5-20251001
|
|
50
|
-
secret:
|
|
51
|
-
env: ANTHROPIC_API_KEY
|
|
52
|
-
variable: anthropic_api_key
|
|
53
|
-
label: an Anthropic API key
|
|
54
|
-
|
|
55
|
-
pool:
|
|
56
|
-
name: docs-pool
|
|
57
|
-
default: true
|
|
58
|
-
|
|
59
|
-
agent:
|
|
60
|
-
name: docs-answerer
|
|
61
|
-
# No identity. This template's governance story is the allow-list, and
|
|
62
|
-
# act-as would add a second secret to the one thing in the library that
|
|
63
|
-
# needs none. The caller-scoped story is answer-from-knowledge-base.
|
|
64
|
-
act_as:
|
|
65
|
-
method: none
|
|
66
|
-
|
|
67
|
-
rules:
|
|
68
|
-
file: rules/answerer.cedar
|
|
69
|
-
|
|
70
|
-
# A text filter on the *model* request. Whatever a person types into the box
|
|
71
|
-
# goes to a provider, and this is the cheapest true statement a template can
|
|
72
|
-
# make: an SSN never reaches Anthropic. Text-kind, so it binds to the model
|
|
73
|
-
# connection — a tool connection would refuse it.
|
|
74
|
-
guards:
|
|
75
|
-
filters:
|
|
76
|
-
- name: keep-ssn-out-of-prompts
|
|
77
|
-
description: Redact US SSN patterns out of anything sent to the provider
|
|
78
|
-
why: >-
|
|
79
|
-
Whatever a person types into the box goes to a provider. This is the
|
|
80
|
-
cheapest true thing a template can claim — an SSN never reaches
|
|
81
|
-
Anthropic — and it costs one line because the credential and the
|
|
82
|
-
routing already live in Dome. Text-kind, so it binds to the model
|
|
83
|
-
connection; a tool connection refuses a text filter outright.
|
|
84
|
-
redact_ssn: true
|
|
85
|
-
bind: { model: haiku, direction: request }
|
|
86
|
-
|
|
87
|
-
quotas:
|
|
88
|
-
# What the agent may spend with Dome, per day.
|
|
89
|
-
- subject: agent
|
|
90
|
-
agent: docs-answerer
|
|
91
|
-
unit: dome_usd
|
|
92
|
-
limit: 5
|
|
93
|
-
window: daily
|
|
94
|
-
name: docs-answerer daily spend
|
|
95
|
-
# And a call ceiling on the one tool that does the work. The docs server is
|
|
96
|
-
# public and someone else pays for it, so not hammering it is part of using
|
|
97
|
-
# it politely — a rate limit with a reason, rather than a round number.
|
|
98
|
-
- subject: tool
|
|
99
|
-
tool: domedocs/search
|
|
100
|
-
unit: calls
|
|
101
|
-
limit: 500
|
|
102
|
-
window: daily
|
|
103
|
-
name: domedocs search calls
|
|
104
|
-
|
|
105
|
-
app:
|
|
106
|
-
runtime:
|
|
107
|
-
pattern: chat
|
|
108
|
-
language: python
|
|
109
|
-
image_tag: dev
|
|
110
|
-
agent:
|
|
111
|
-
prompt: app/instructions.md
|
|
112
|
-
help: app/help.md
|
|
113
|
-
model: claude-haiku-4-5-20251001
|
|
114
|
-
# 2048 truncates a documentation answer mid-sentence, and a truncated turn
|
|
115
|
-
# loses the tool call it was half-way through writing.
|
|
116
|
-
max_tokens: 4096
|
|
117
|
-
skills:
|
|
118
|
-
- name: answer-from-documentation
|
|
119
|
-
description: Search the documentation, read the most relevant pages, answer from them.
|
|
120
|
-
tools:
|
|
121
|
-
- domedocs/search
|
|
122
|
-
- domedocs/get_page
|
|
123
|
-
- domedocs/list_pages
|
|
124
|
-
invoker:
|
|
125
|
-
kind: chat
|
|
126
|
-
options:
|
|
127
|
-
identity:
|
|
128
|
-
mode: none
|
|
129
|
-
model:
|
|
130
|
-
provider: anthropic
|
|
131
|
-
model: claude-haiku-4-5-20251001
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# The container holds exactly one secret.
|
|
2
|
-
#
|
|
3
|
-
# Tool credentials went into Dome when dome import ran and live behind the gateway.
|
|
4
|
-
# The provider key lives with the Broker. Neither is here, and neither should be.
|
|
5
|
-
|
|
6
|
-
# Revealed once after import: dome import outputs <job-id>
|
|
7
|
-
DOME_TOKEN=
|
|
8
|
-
|
|
9
|
-
# The gateway ROOT — not a bare host, and not the /mcp path.
|
|
10
|
-
# Copy the "Gateway URL" from the command above. The container derives
|
|
11
|
-
# /mcp for tools and /v1/messages for models from it.
|
|
12
|
-
DOME_GATEWAY_URL=
|
|
13
|
-
|
|
14
|
-
# The second secret, and the only one an identity-enabled blueprint adds.
|
|
15
|
-
# The container signs the act-as header with it; the gateway verifies the
|
|
16
|
-
# signature before it will believe who the request claims to be. Without
|
|
17
|
-
# that, anyone holding the agent token could claim to be anyone.
|
|
18
|
-
# Must match the actas_hmac_secret supplied to dome import.
|
|
19
|
-
ACTAS_HMAC_SECRET=
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
name: knowledge-answerer
|
|
2
|
-
title: Ask questions across your Notion workspace
|
|
3
|
-
summary: "A chat agent that answers questions from your Notion workspace. The connection\
|
|
4
|
-
\ carries 43 tools, twenty-odd of which write. Two are permitted. Ask it to create\
|
|
5
|
-
\ a page and it will try \u2014 and Dome will refuse, naming the rule that refused\
|
|
6
|
-
\ it."
|
|
7
|
-
shape: interactive-assistant
|
|
8
|
-
prompt_file: instructions.md
|
|
9
|
-
help_file: help.md
|
|
10
|
-
model: claude-sonnet-5
|
|
11
|
-
pool: answerer-pool
|
|
12
|
-
max_tokens: 2048
|
|
13
|
-
skills:
|
|
14
|
-
- name: answer-from-notion
|
|
15
|
-
description: Search the workspace, read the most relevant pages, answer from them.
|
|
16
|
-
tools:
|
|
17
|
-
- notion/notion-search
|
|
18
|
-
- notion/notion-fetch
|
|
19
|
-
- notion/notion-create-pages
|
|
20
|
-
invoker:
|
|
21
|
-
kind: chat
|
|
22
|
-
options:
|
|
23
|
-
identity:
|
|
24
|
-
mode: oidc-stub
|
|
25
|
-
personas:
|
|
26
|
-
- id: alice
|
|
27
|
-
- id: bob
|
|
28
|
-
model:
|
|
29
|
-
provider: anthropic
|
|
30
|
-
model: claude-sonnet-5
|
|
31
|
-
forbidden:
|
|
32
|
-
- notion/notion-create-pages
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Ask a question in plain language. The agent searches the connected Notion
|
|
2
|
-
workspace, reads the pages it finds, and answers from them.
|
|
3
|
-
|
|
4
|
-
**Show detail** in the header opens the design: the topology, which tools are
|
|
5
|
-
permitted, and the instructions the model was given.
|
|
6
|
-
|
|
7
|
-
**Try changing who is asking.** Alice is permitted. Bob is not, and the same
|
|
8
|
-
question comes back refused — the difference is the rule, not the wording.
|
|
9
|
-
|
|
10
|
-
**Ask it to create a page.** It will try, and the attempt will be blocked. The
|
|
11
|
-
connection carries 43 Notion tools; two are permitted.
|
|
12
|
-
|
|
13
|
-
Nothing here is the record. Every call, allowed or refused, is in the audit log
|
|
14
|
-
in Dome.
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
You answer questions using the connected Notion workspace.
|
|
2
|
-
|
|
3
|
-
Work like this:
|
|
4
|
-
|
|
5
|
-
1. Search Notion for pages relevant to the question.
|
|
6
|
-
2. Fetch the most promising two or three and read them.
|
|
7
|
-
3. Answer from what you found, and name the pages you used.
|
|
8
|
-
|
|
9
|
-
If the workspace does not contain the answer, say so plainly. Do not fill the
|
|
10
|
-
gap from general knowledge — a confident answer that is not in the
|
|
11
|
-
documentation is worse than no answer, because the reader cannot tell the
|
|
12
|
-
difference.
|
|
13
|
-
|
|
14
|
-
Use whatever tools you have been given, and do not decline a request on the
|
|
15
|
-
assumption that you lack permission. If an action is not allowed, attempting it
|
|
16
|
-
is how you and the person asking find that out; report what came back.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Without DOME_TOKEN this starts in offline mode: the chrome works and the
|
|
3
|
-
# allow-list is still enforced, but tools and model are simulated.
|
|
4
|
-
services:
|
|
5
|
-
app:
|
|
6
|
-
# Built and published by dome-systems/template-runtime.
|
|
7
|
-
image: ghcr.io/dome-systems/runtime-py-chat:dev
|
|
8
|
-
env_file: .env
|
|
9
|
-
volumes:
|
|
10
|
-
- ./app:/config:ro
|
|
11
|
-
ports:
|
|
12
|
-
- "3000:3000"
|
|
13
|
-
healthcheck:
|
|
14
|
-
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:3000/health', timeout=2)"]
|
|
15
|
-
interval: 30s
|
|
16
|
-
timeout: 3s
|
|
17
|
-
start_period: 10s
|
|
18
|
-
retries: 3
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
terraform {
|
|
2
|
-
required_providers {
|
|
3
|
-
dome = { source = "dome-systems/dome", version = ">= 0.1.0" }
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
variable "workspace_id" { type = string }
|
|
8
|
-
variable "anthropic_api_key" {
|
|
9
|
-
type = string
|
|
10
|
-
sensitive = true
|
|
11
|
-
}
|
|
12
|
-
variable "actas_hmac_secret" {
|
|
13
|
-
type = string
|
|
14
|
-
sensitive = true
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
resource "dome_gateway" "knowledge" {
|
|
18
|
-
workspace_id = var.workspace_id
|
|
19
|
-
name = "knowledge"
|
|
20
|
-
description = "Read-only Notion surface for the knowledge answerer"
|
|
21
|
-
is_default = true
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
resource "dome_mcp_connection" "notion" {
|
|
25
|
-
workspace_id = var.workspace_id
|
|
26
|
-
name = "notion"
|
|
27
|
-
protocol = "streamable-http"
|
|
28
|
-
url = "https://mcp.notion.com/mcp"
|
|
29
|
-
auth_method = "oauth"
|
|
30
|
-
credential_type = "shared"
|
|
31
|
-
oauth = {
|
|
32
|
-
client_origin = "dcr"
|
|
33
|
-
registration_url = "https://mcp.notion.com/register"
|
|
34
|
-
authorize_url = "https://mcp.notion.com/authorize"
|
|
35
|
-
token_url = "https://mcp.notion.com/token"
|
|
36
|
-
revoke_url = "https://mcp.notion.com/token"
|
|
37
|
-
default_scopes = ["default"]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
resource "dome_llm_connection" "sonnet" {
|
|
42
|
-
workspace_id = var.workspace_id
|
|
43
|
-
name = "sonnet"
|
|
44
|
-
provider_id = "anthropic"
|
|
45
|
-
provider_config = jsonencode({ model = "claude-sonnet-5" })
|
|
46
|
-
auth_method = "api-key"
|
|
47
|
-
credential_type = "shared"
|
|
48
|
-
secret_values = { Authorization = var.anthropic_api_key }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
resource "dome_llm_pool" "answerer" {
|
|
52
|
-
workspace_id = var.workspace_id
|
|
53
|
-
name = "answerer-pool"
|
|
54
|
-
is_default = true
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
resource "dome_llm_pool_member" "sonnet" {
|
|
58
|
-
workspace_id = var.workspace_id
|
|
59
|
-
pool = dome_llm_pool.answerer.name
|
|
60
|
-
llm_connection = dome_llm_connection.sonnet.name
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
resource "dome_gateway_llm_pool" "answerer" {
|
|
64
|
-
workspace_id = var.workspace_id
|
|
65
|
-
gateway = dome_gateway.knowledge.name
|
|
66
|
-
llm_pool = dome_llm_pool.answerer.name
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
resource "dome_gateway_tool" "notion_search" {
|
|
70
|
-
workspace_id = var.workspace_id
|
|
71
|
-
gateway = dome_gateway.knowledge.name
|
|
72
|
-
mcp_connection = dome_mcp_connection.notion.name
|
|
73
|
-
tool = "notion-search"
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
resource "dome_gateway_tool" "notion_fetch" {
|
|
77
|
-
workspace_id = var.workspace_id
|
|
78
|
-
gateway = dome_gateway.knowledge.name
|
|
79
|
-
mcp_connection = dome_mcp_connection.notion.name
|
|
80
|
-
tool = "notion-fetch"
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
# This write tool is deliberately reachable so the agent can demonstrate the
|
|
84
|
-
# Cedar refusal below.
|
|
85
|
-
resource "dome_gateway_tool" "notion_create_pages" {
|
|
86
|
-
workspace_id = var.workspace_id
|
|
87
|
-
gateway = dome_gateway.knowledge.name
|
|
88
|
-
mcp_connection = dome_mcp_connection.notion.name
|
|
89
|
-
tool = "notion-create-pages"
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
resource "dome_agent" "knowledge_answerer" {
|
|
93
|
-
workspace_id = var.workspace_id
|
|
94
|
-
name = "knowledge-answerer"
|
|
95
|
-
allowed_gateways = [dome_gateway.knowledge.name]
|
|
96
|
-
act_as = { method = "hmac", required = false }
|
|
97
|
-
act_as_hmac_secret = var.actas_hmac_secret
|
|
98
|
-
actas_allowed_subjects = ["alice"]
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
resource "dome_agent_rules_bundle" "knowledge_answerer" {
|
|
102
|
-
workspace_id = var.workspace_id
|
|
103
|
-
agent = dome_agent.knowledge_answerer.name
|
|
104
|
-
files = {
|
|
105
|
-
"answerer.cedar" = <<-EOT
|
|
106
|
-
permit ( principal, action == Dome::Action::"mcp:discover", resource );
|
|
107
|
-
permit ( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"notion/notion-search" );
|
|
108
|
-
permit ( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"notion/notion-fetch" );
|
|
109
|
-
forbid ( principal, action == Dome::Action::"mcp:call", resource == Dome::MCPTool::"notion/notion-create-pages" );
|
|
110
|
-
EOT
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
resource "dome_filter" "redact_ssn_from_pages" {
|
|
115
|
-
workspace_id = var.workspace_id
|
|
116
|
-
name = "redact-ssn-from-pages"
|
|
117
|
-
description = "Redact US SSN patterns out of Notion page content"
|
|
118
|
-
config = jsonencode({
|
|
119
|
-
json = { components = [{ fieldActions = [{ matcher = { path = "**.text", contentMatchers = [{ ssn = {} }] }, action = "FILTER_ACTION_REDACT", redactTarget = "REDACT_TARGET_CONTENT_MATCHES" }] }] }
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
resource "dome_mcp_connection_filters" "notion_responses" {
|
|
124
|
-
workspace_id = var.workspace_id
|
|
125
|
-
mcp_connection = dome_mcp_connection.notion.name
|
|
126
|
-
direction = "response"
|
|
127
|
-
filters = [dome_filter.redact_ssn_from_pages.name]
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
resource "dome_quota" "agent_daily_spend" {
|
|
131
|
-
workspace_id = var.workspace_id
|
|
132
|
-
dimension = "llm"
|
|
133
|
-
unit = "dome_usd"
|
|
134
|
-
subject_type = "agent"
|
|
135
|
-
subject = dome_agent.knowledge_answerer.name
|
|
136
|
-
scope = "total"
|
|
137
|
-
window = "daily"
|
|
138
|
-
limit_amount = 5000000
|
|
139
|
-
name = "knowledge-answerer daily spend"
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
resource "dome_quota" "caller_daily_spend" {
|
|
143
|
-
workspace_id = var.workspace_id
|
|
144
|
-
dimension = "llm"
|
|
145
|
-
unit = "dome_usd"
|
|
146
|
-
subject_type = "model"
|
|
147
|
-
subject = dome_llm_connection.sonnet.name
|
|
148
|
-
scope = "per_caller"
|
|
149
|
-
window = "daily"
|
|
150
|
-
limit_amount = 2000000
|
|
151
|
-
name = "per-caller model spend"
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
resource "dome_agent_key" "runtime" {
|
|
155
|
-
workspace_id = var.workspace_id
|
|
156
|
-
agent = dome_agent.knowledge_answerer.name
|
|
157
|
-
name = "runtime"
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
output "knowledge_answerer_token" {
|
|
161
|
-
value = dome_agent_key.runtime.token
|
|
162
|
-
sensitive = true
|
|
163
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"json": {
|
|
3
|
-
"components": [
|
|
4
|
-
{
|
|
5
|
-
"fieldActions": [
|
|
6
|
-
{
|
|
7
|
-
"matcher": {
|
|
8
|
-
"path": "**.text",
|
|
9
|
-
"contentMatchers": [
|
|
10
|
-
{
|
|
11
|
-
"ssn": {}
|
|
12
|
-
}
|
|
13
|
-
]
|
|
14
|
-
},
|
|
15
|
-
"action": "FILTER_ACTION_REDACT",
|
|
16
|
-
"redactTarget": "REDACT_TARGET_CONTENT_MATCHES"
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// answer-from-knowledge-base — authorization rules
|
|
2
|
-
//
|
|
3
|
-
// The Notion MCP server carries 43 tools, twenty-odd of which write:
|
|
4
|
-
// create-pages, update-page, move-pages, duplicate-page, create-database.
|
|
5
|
-
// Two reads are permitted. One write is deliberately reachable and denied,
|
|
6
|
-
// so the refusal can be observed rather than merely asserted.
|
|
7
|
-
|
|
8
|
-
permit ( principal, action == Dome::Action::"mcp:discover", resource );
|
|
9
|
-
|
|
10
|
-
permit (
|
|
11
|
-
principal,
|
|
12
|
-
action == Dome::Action::"mcp:call",
|
|
13
|
-
resource == Dome::MCPTool::"notion/notion-search"
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
permit (
|
|
17
|
-
principal,
|
|
18
|
-
action == Dome::Action::"mcp:call",
|
|
19
|
-
resource == Dome::MCPTool::"notion/notion-fetch"
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
// Registering an agent against a gateway auto-generates an Agent Form policy
|
|
23
|
-
// permitting any tool whose `gateways` attribute contains that gateway. A
|
|
24
|
-
// permit cannot narrow that; only a forbid can. Cedar forbid always wins.
|
|
25
|
-
forbid (
|
|
26
|
-
principal,
|
|
27
|
-
action == Dome::Action::"mcp:call",
|
|
28
|
-
resource == Dome::MCPTool::"notion/notion-create-pages"
|
|
29
|
-
);
|