@emilia-protocol/gate 0.6.0 → 0.7.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.
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * EMILIA Gate — Cloudflare System-of-Record adapter.
4
+ * Guards DNS record delete, zone delete, and WAF/firewall disable so they never
5
+ * reach Cloudflare without a receipt bound to THIS zone. Client injected.
6
+ */
7
+ import { createAdapter, manifestFromPack } from './_kit.js';
8
+
9
+ export const CLOUDFLARE_ACTION_PACK = Object.freeze([
10
+ Object.freeze({
11
+ id: 'cloudflare.dns.delete', label: 'Delete DNS record', action_type: 'cloudflare.dns.delete',
12
+ risk: 'high', receipt_required: true, assurance_class: 'class_a',
13
+ match: { protocol: 'cloudflare', tool: 'delete_dns_record' },
14
+ why: 'Removing DNS can take a service offline or enable takeover. Bind zone+record.',
15
+ execution_binding: { required_fields: ['action_type', 'zone', 'record_id'] },
16
+ }),
17
+ Object.freeze({
18
+ id: 'cloudflare.zone.delete', label: 'Delete zone', action_type: 'cloudflare.zone.delete',
19
+ risk: 'critical', receipt_required: true, assurance_class: 'quorum',
20
+ match: { protocol: 'cloudflare', tool: 'delete_zone' },
21
+ why: 'Deletes an entire zone. Quorum.',
22
+ execution_binding: { required_fields: ['action_type', 'zone'] },
23
+ }),
24
+ Object.freeze({
25
+ id: 'cloudflare.firewall.disable', label: 'Disable firewall rule', action_type: 'cloudflare.firewall.disable',
26
+ risk: 'critical', receipt_required: true, assurance_class: 'quorum',
27
+ match: { protocol: 'cloudflare', tool: 'set_firewall_rule' },
28
+ why: 'Disabling WAF/firewall opens the perimeter. Quorum + bind zone+rule.',
29
+ execution_binding: { required_fields: ['action_type', 'zone', 'rule_id'] },
30
+ }),
31
+ ]);
32
+
33
+ const OPS = {
34
+ 'dns.delete': {
35
+ selector: { protocol: 'cloudflare', tool: 'delete_dns_record' },
36
+ observed: (p) => ({ action_type: 'cloudflare.dns.delete', zone: p.zone, record_id: p.record_id }),
37
+ perform: (c, p) => c.deleteDnsRecord({ zone: p.zone, recordId: p.record_id }),
38
+ },
39
+ 'zone.delete': {
40
+ selector: { protocol: 'cloudflare', tool: 'delete_zone' },
41
+ observed: (p) => ({ action_type: 'cloudflare.zone.delete', zone: p.zone }),
42
+ perform: (c, p) => c.deleteZone({ zone: p.zone }),
43
+ },
44
+ 'firewall.disable': {
45
+ selector: { protocol: 'cloudflare', tool: 'set_firewall_rule' },
46
+ observed: (p) => ({ action_type: 'cloudflare.firewall.disable', zone: p.zone, rule_id: p.rule_id }),
47
+ perform: (c, p) => c.setFirewallRule({ zone: p.zone, ruleId: p.rule_id, enabled: false }),
48
+ },
49
+ };
50
+
51
+ const adapter = createAdapter({ system: 'cloudflare', ops: OPS });
52
+ export const CLOUDFLARE_OPS = adapter.OPS;
53
+ export function createCloudflareManifest(extra = []) { return manifestFromPack(CLOUDFLARE_ACTION_PACK, extra); }
54
+ export function guardCloudflareMutation(gate, client, args) { return adapter.guard(gate, client, args); }
55
+ export default { CLOUDFLARE_ACTION_PACK, CLOUDFLARE_OPS, createCloudflareManifest, guardCloudflareMutation };
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * EMILIA Gate — Jira System-of-Record adapter.
4
+ * Guards bulk issue delete, project delete, and permission grants so they never
5
+ * reach Jira without a receipt bound to THIS resource. Client injected.
6
+ */
7
+ import { createAdapter, manifestFromPack, hashCanonical } from './_kit.js';
8
+
9
+ export const JIRA_ACTION_PACK = Object.freeze([
10
+ Object.freeze({
11
+ id: 'jira.issue.bulk_delete', label: 'Bulk delete issues', action_type: 'jira.issue.bulk_delete',
12
+ risk: 'critical', receipt_required: true, assurance_class: 'class_a',
13
+ match: { protocol: 'jira', tool: 'bulk_delete_issues' },
14
+ why: 'Mass-destroys issues. Bind the exact JQL.',
15
+ execution_binding: { required_fields: ['action_type', 'project', 'jql_hash'] },
16
+ }),
17
+ Object.freeze({
18
+ id: 'jira.project.delete', label: 'Delete project', action_type: 'jira.project.delete',
19
+ risk: 'critical', receipt_required: true, assurance_class: 'quorum',
20
+ match: { protocol: 'jira', tool: 'delete_project' },
21
+ why: 'Deletes a project and its issues. Quorum.',
22
+ execution_binding: { required_fields: ['action_type', 'project_key'] },
23
+ }),
24
+ Object.freeze({
25
+ id: 'jira.permission.grant', label: 'Grant permission', action_type: 'jira.permission.grant',
26
+ risk: 'critical', receipt_required: true, assurance_class: 'quorum',
27
+ match: { protocol: 'jira', tool: 'grant_permission' },
28
+ why: 'Changes who can act. Quorum + bind project/principal/role.',
29
+ execution_binding: { required_fields: ['action_type', 'project', 'principal', 'role'] },
30
+ }),
31
+ ]);
32
+
33
+ const OPS = {
34
+ 'issue.bulk_delete': {
35
+ selector: { protocol: 'jira', tool: 'bulk_delete_issues' },
36
+ observed: (p) => ({ action_type: 'jira.issue.bulk_delete', project: p.project, jql_hash: hashCanonical(String(p.jql || '').trim()) }),
37
+ perform: (c, p) => c.bulkDeleteIssues({ project: p.project, jql: p.jql }),
38
+ },
39
+ 'project.delete': {
40
+ selector: { protocol: 'jira', tool: 'delete_project' },
41
+ observed: (p) => ({ action_type: 'jira.project.delete', project_key: p.project_key }),
42
+ perform: (c, p) => c.deleteProject({ projectKey: p.project_key }),
43
+ },
44
+ 'permission.grant': {
45
+ selector: { protocol: 'jira', tool: 'grant_permission' },
46
+ observed: (p) => ({ action_type: 'jira.permission.grant', project: p.project, principal: p.principal, role: p.role }),
47
+ perform: (c, p) => c.grantPermission({ project: p.project, principal: p.principal, role: p.role }),
48
+ },
49
+ };
50
+
51
+ const adapter = createAdapter({ system: 'jira', ops: OPS });
52
+ export const JIRA_OPS = adapter.OPS;
53
+ export function createJiraManifest(extra = []) { return manifestFromPack(JIRA_ACTION_PACK, extra); }
54
+ export function guardJiraMutation(gate, client, args) { return adapter.guard(gate, client, args); }
55
+ export default { JIRA_ACTION_PACK, JIRA_OPS, createJiraManifest, guardJiraMutation };
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * EMILIA Gate — Linear System-of-Record adapter.
4
+ * Guards issue deletion, bulk delete, and team deletion so they never reach
5
+ * Linear without a receipt bound to THIS resource. Client injected.
6
+ */
7
+ import { createAdapter, manifestFromPack, hashCanonical } from './_kit.js';
8
+
9
+ export const LINEAR_ACTION_PACK = Object.freeze([
10
+ Object.freeze({
11
+ id: 'linear.issue.delete', label: 'Delete issue', action_type: 'linear.issue.delete',
12
+ risk: 'high', receipt_required: true, assurance_class: 'class_a',
13
+ match: { protocol: 'linear', tool: 'delete_issue' },
14
+ why: 'Destroys an issue. Bind the issue id.',
15
+ execution_binding: { required_fields: ['action_type', 'issue_id'] },
16
+ }),
17
+ Object.freeze({
18
+ id: 'linear.issue.bulk_delete', label: 'Bulk delete issues', action_type: 'linear.issue.bulk_delete',
19
+ risk: 'critical', receipt_required: true, assurance_class: 'class_a',
20
+ match: { protocol: 'linear', tool: 'bulk_delete_issues' },
21
+ why: 'Mass-destroys issues. Bind the exact query.',
22
+ execution_binding: { required_fields: ['action_type', 'team', 'query_hash'] },
23
+ }),
24
+ Object.freeze({
25
+ id: 'linear.team.delete', label: 'Delete team', action_type: 'linear.team.delete',
26
+ risk: 'critical', receipt_required: true, assurance_class: 'quorum',
27
+ match: { protocol: 'linear', tool: 'delete_team' },
28
+ why: 'Deletes a team and its issues. Quorum.',
29
+ execution_binding: { required_fields: ['action_type', 'team_id'] },
30
+ }),
31
+ ]);
32
+
33
+ const OPS = {
34
+ 'issue.delete': {
35
+ selector: { protocol: 'linear', tool: 'delete_issue' },
36
+ observed: (p) => ({ action_type: 'linear.issue.delete', issue_id: p.issue_id }),
37
+ perform: (c, p) => c.deleteIssue({ issueId: p.issue_id }),
38
+ },
39
+ 'issue.bulk_delete': {
40
+ selector: { protocol: 'linear', tool: 'bulk_delete_issues' },
41
+ observed: (p) => ({ action_type: 'linear.issue.bulk_delete', team: p.team, query_hash: hashCanonical(String(p.query || '').trim()) }),
42
+ perform: (c, p) => c.bulkDeleteIssues({ team: p.team, query: p.query }),
43
+ },
44
+ 'team.delete': {
45
+ selector: { protocol: 'linear', tool: 'delete_team' },
46
+ observed: (p) => ({ action_type: 'linear.team.delete', team_id: p.team_id }),
47
+ perform: (c, p) => c.deleteTeam({ teamId: p.team_id }),
48
+ },
49
+ };
50
+
51
+ const adapter = createAdapter({ system: 'linear', ops: OPS });
52
+ export const LINEAR_OPS = adapter.OPS;
53
+ export function createLinearManifest(extra = []) { return manifestFromPack(LINEAR_ACTION_PACK, extra); }
54
+ export function guardLinearMutation(gate, client, args) { return adapter.guard(gate, client, args); }
55
+ export default { LINEAR_ACTION_PACK, LINEAR_OPS, createLinearManifest, guardLinearMutation };
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * EMILIA Gate — Salesforce System-of-Record adapter.
4
+ * Guards bulk record delete, data export, and permission-set assignment so they
5
+ * never reach Salesforce without a receipt bound to THIS object. Client injected.
6
+ */
7
+ import { createAdapter, manifestFromPack, hashCanonical } from './_kit.js';
8
+
9
+ export const SALESFORCE_ACTION_PACK = Object.freeze([
10
+ Object.freeze({
11
+ id: 'salesforce.records.bulk_delete', label: 'Bulk delete records', action_type: 'salesforce.records.bulk_delete',
12
+ risk: 'critical', receipt_required: true, assurance_class: 'class_a',
13
+ match: { protocol: 'salesforce', tool: 'bulk_delete' },
14
+ why: 'Mass-destroys CRM records. Bind object + the exact SOQL.',
15
+ execution_binding: { required_fields: ['action_type', 'object', 'soql_hash'] },
16
+ }),
17
+ Object.freeze({
18
+ id: 'salesforce.data.export', label: 'Bulk data export', action_type: 'salesforce.data.export',
19
+ risk: 'high', receipt_required: true, assurance_class: 'class_a',
20
+ match: { protocol: 'salesforce', tool: 'export' },
21
+ why: 'Exfiltrates CRM data. Bind object + recipient.',
22
+ execution_binding: { required_fields: ['action_type', 'object', 'recipient'] },
23
+ }),
24
+ Object.freeze({
25
+ id: 'salesforce.permission_set.assign', label: 'Assign permission set', action_type: 'salesforce.permission_set.assign',
26
+ risk: 'critical', receipt_required: true, assurance_class: 'quorum',
27
+ match: { protocol: 'salesforce', tool: 'assign_permission_set' },
28
+ why: 'Grants privileges. Quorum + bind user + permission set.',
29
+ execution_binding: { required_fields: ['action_type', 'user', 'permission_set'] },
30
+ }),
31
+ ]);
32
+
33
+ const OPS = {
34
+ 'records.bulk_delete': {
35
+ selector: { protocol: 'salesforce', tool: 'bulk_delete' },
36
+ observed: (p) => ({ action_type: 'salesforce.records.bulk_delete', object: p.object, soql_hash: hashCanonical(String(p.soql || '').trim()) }),
37
+ perform: (c, p) => c.bulkDelete({ object: p.object, soql: p.soql }),
38
+ },
39
+ 'data.export': {
40
+ selector: { protocol: 'salesforce', tool: 'export' },
41
+ observed: (p) => ({ action_type: 'salesforce.data.export', object: p.object, recipient: p.recipient }),
42
+ perform: (c, p) => c.export({ object: p.object, recipient: p.recipient }),
43
+ },
44
+ 'permission_set.assign': {
45
+ selector: { protocol: 'salesforce', tool: 'assign_permission_set' },
46
+ observed: (p) => ({ action_type: 'salesforce.permission_set.assign', user: p.user, permission_set: p.permission_set }),
47
+ perform: (c, p) => c.assignPermissionSet({ user: p.user, permissionSet: p.permission_set }),
48
+ },
49
+ };
50
+
51
+ const adapter = createAdapter({ system: 'salesforce', ops: OPS });
52
+ export const SALESFORCE_OPS = adapter.OPS;
53
+ export function createSalesforceManifest(extra = []) { return manifestFromPack(SALESFORCE_ACTION_PACK, extra); }
54
+ export function guardSalesforceMutation(gate, client, args) { return adapter.guard(gate, client, args); }
55
+ export default { SALESFORCE_ACTION_PACK, SALESFORCE_OPS, createSalesforceManifest, guardSalesforceMutation };
@@ -0,0 +1,56 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * EMILIA Gate — Vercel System-of-Record adapter.
4
+ * Guards promote-to-production, project delete, and env/secret changes so they
5
+ * never reach Vercel without a receipt bound to THIS project. Client injected
6
+ * (the Vercel REST client or a thin wrapper).
7
+ */
8
+ import { createAdapter, manifestFromPack } from './_kit.js';
9
+
10
+ export const VERCEL_ACTION_PACK = Object.freeze([
11
+ Object.freeze({
12
+ id: 'vercel.deploy.promote', label: 'Promote to production', action_type: 'vercel.deploy.promote',
13
+ risk: 'critical', receipt_required: true, assurance_class: 'quorum',
14
+ match: { protocol: 'vercel', tool: 'promote_deployment' },
15
+ why: 'Changes live production. Quorum for the prod cutover.',
16
+ execution_binding: { required_fields: ['action_type', 'project', 'deployment_id'] },
17
+ }),
18
+ Object.freeze({
19
+ id: 'vercel.project.delete', label: 'Delete project', action_type: 'vercel.project.delete',
20
+ risk: 'critical', receipt_required: true, assurance_class: 'class_a',
21
+ match: { protocol: 'vercel', tool: 'delete_project' },
22
+ why: 'Destroys a project and its deployments. Bind the project.',
23
+ execution_binding: { required_fields: ['action_type', 'project'] },
24
+ }),
25
+ Object.freeze({
26
+ id: 'vercel.env.set', label: 'Set env / secret', action_type: 'vercel.env.set',
27
+ risk: 'high', receipt_required: true, assurance_class: 'class_a',
28
+ match: { protocol: 'vercel', tool: 'upsert_env' },
29
+ why: 'Changes production secrets/config. Bind project+key+target.',
30
+ execution_binding: { required_fields: ['action_type', 'project', 'key', 'target'] },
31
+ }),
32
+ ]);
33
+
34
+ const OPS = {
35
+ 'deploy.promote': {
36
+ selector: { protocol: 'vercel', tool: 'promote_deployment' },
37
+ observed: (p) => ({ action_type: 'vercel.deploy.promote', project: p.project, deployment_id: p.deployment_id }),
38
+ perform: (c, p) => c.promoteDeployment({ project: p.project, deploymentId: p.deployment_id }),
39
+ },
40
+ 'project.delete': {
41
+ selector: { protocol: 'vercel', tool: 'delete_project' },
42
+ observed: (p) => ({ action_type: 'vercel.project.delete', project: p.project }),
43
+ perform: (c, p) => c.deleteProject({ project: p.project }),
44
+ },
45
+ 'env.set': {
46
+ selector: { protocol: 'vercel', tool: 'upsert_env' },
47
+ observed: (p) => ({ action_type: 'vercel.env.set', project: p.project, key: p.key, target: p.target }),
48
+ perform: (c, p) => c.upsertEnv({ project: p.project, key: p.key, value: p.value, target: p.target }),
49
+ },
50
+ };
51
+
52
+ const adapter = createAdapter({ system: 'vercel', ops: OPS });
53
+ export const VERCEL_OPS = adapter.OPS;
54
+ export function createVercelManifest(extra = []) { return manifestFromPack(VERCEL_ACTION_PACK, extra); }
55
+ export function guardVercelMutation(gate, client, args) { return adapter.guard(gate, client, args); }
56
+ export default { VERCEL_ACTION_PACK, VERCEL_OPS, createVercelManifest, guardVercelMutation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emilia-protocol/gate",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "EMILIA Gate — the Trusted Action Firewall. Deny-by-default enforcement for consequential machine actions: an action runs only with a valid, in-scope, sufficiently-assured, non-replayed EMILIA authorization receipt (proof a named human authorized this exact action). Composes require-receipt + verify; adds assurance-tier enforcement, one-time consumption (replay defense), a tamper-evident evidence log, default high-risk action packs, execution-field binding, reliance packets, an MCP drop-in, a GitHub system-of-record adapter, and EG-1 conformance. Framework-agnostic, fails closed. Reference implementation, experimental.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -15,11 +15,16 @@
15
15
  "./adapters/k8s": "./adapters/k8s.js",
16
16
  "./adapters/terraform": "./adapters/terraform.js",
17
17
  "./adapters/gcp": "./adapters/gcp.js",
18
+ "./adapters/vercel": "./adapters/vercel.js",
19
+ "./adapters/cloudflare": "./adapters/cloudflare.js",
20
+ "./adapters/linear": "./adapters/linear.js",
21
+ "./adapters/jira": "./adapters/jira.js",
22
+ "./adapters/salesforce": "./adapters/salesforce.js",
18
23
  "./key-registry": "./key-registry.js",
19
24
  "./retention": "./retention.js",
20
25
  "./package.json": "./package.json"
21
26
  },
22
- "files": ["index.js", "store.js", "evidence.js", "action-packs.js", "execution-binding.js", "reliance-packet.js", "key-registry.js", "retention.js", "eg1-conformance.js", "eg1.mjs", "mcp.js", "adapters/_kit.js", "adapters/github.js", "adapters/github-demo.mjs", "adapters/stripe.js", "adapters/supabase.js", "adapters/aws.js", "adapters/k8s.js", "adapters/terraform.js", "adapters/gcp.js", "demo.mjs", "custody-demo.mjs", "README.md"],
27
+ "files": ["index.js", "store.js", "evidence.js", "action-packs.js", "execution-binding.js", "reliance-packet.js", "key-registry.js", "retention.js", "eg1-conformance.js", "eg1.mjs", "mcp.js", "adapters/_kit.js", "adapters/github.js", "adapters/github-demo.mjs", "adapters/stripe.js", "adapters/supabase.js", "adapters/aws.js", "adapters/k8s.js", "adapters/terraform.js", "adapters/gcp.js", "adapters/vercel.js", "adapters/cloudflare.js", "adapters/linear.js", "adapters/jira.js", "adapters/salesforce.js", "demo.mjs", "custody-demo.mjs", "README.md"],
23
28
  "scripts": {
24
29
  "test": "node --test",
25
30
  "demo": "node demo.mjs",