{
  "schema": "aaes/capability-pack/1",
  "provider": "mysql",
  "version": "1.0.0",
  "description": "MySQL: query one table and insert, update or delete its rows through the native MySQL adapter and a deployment statement allowlist. Draft awaiting owner review.",
  "notes": "Draft awaiting owner review. Select the MySQL adapter with native_executors.sql.engine set to mysql; every SQL capability in that deployment must then use a mysql:// endpoint, and a registration for another engine is refused when the configuration loads. The adapter (aaes/internal/connectors/native_mysql.go, go-sql-driver/mysql v1.10.1, reviewed in ADR-046) connects only to the registered host, port and database. It resolves the host through the connectors' pinned egress dial (public addresses, RFC 1918 and unique-local addresses only with egress_private_networks), requires TLS 1.2 or later with the certificate verified against the registered host name, and refuses a server that does not offer TLS before sending credentials. Parameters are bound as server-side prepared statement arguments and never interpolated. Statements must match the deployment allowlist byte for byte, must start with SELECT, INSERT, UPDATE, DELETE, REPLACE or WITH, and may not name INTO OUTFILE, INTO DUMPFILE, INFILE or LOAD_FILE; multi-statement strings, comments and CALL are refused.\n\nA query capability runs with the session variable transaction_read_only set to ON and inside START TRANSACTION READ ONLY, and the transaction is always rolled back. MySQL then refuses changes to tables visible to other transactions, including writes made by a stored function the statement calls. TEMPORARY tables stay writable in that mode, so the login's grants remain a required control. The adapter needs MySQL 8.0 or later (transaction_read_only); it was tested against MySQL 8.4.\n\nDB_HOST, DB_NAME and TABLE_NAME are placeholders. MySQL listens on TCP 3306 by default (MySQL's connection guide names 3306 as the default port); substitute the real host, port and database in the endpoint, the egress allowlist and the resource locator together.\n\nRead operations are registered at R0, inserts and updates at R2, and deletion at R3. On supported AAES-mediated execution paths, delete requires approval by an authorized person; bypass and observation-only paths are outside that guarantee. The SQL connector's method vocabulary is query or exec, not HTTP methods. A capability named row.insert is a promise about the statement the deployment allowlist will permit, not a check this file makes. Every capability touches one table resource classified pii as a starting point; a deployment whose table holds no personal data may lower it, and one holding payment or health data must raise it.\n\nLeast privilege lives in the database, not in this pack: grant the login SELECT on the one table for query capabilities, and INSERT, UPDATE or DELETE on that table only for the write capabilities you enable. Review the login's grants and the statement allowlist before enabling any write.\n\nAAES has not been certified by the database vendor; the endpoints here are configuration to review and test, and the vendor has not endorsed it.",
  "credential": {
    "secret_ref_id": "secret-ref-mysql-role",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "A dedicated least-privilege MySQL login held in the client's own secret manager. The adapter reads the material as strict JSON with exactly user and password, presents it only during the TLS-protected login and never interpolates it into a statement. A result that echoes the password is refused. Integrated, Kerberos, cleartext-plugin and certificate authentication are not covered."
  },
  "capabilities": [
    {
      "capability_id": "mysql.table.query",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:3306"
      ],
      "endpoint": "mysql://DB_HOST:3306/DB_NAME",
      "method": "query",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mysql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "mysql-table",
      "description": "Run an allowlisted SELECT against the table. DB_HOST, DB_NAME and the table behind mysql-table are placeholders.",
      "requires_review": true
    },
    {
      "capability_id": "mysql.row.insert",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:3306"
      ],
      "endpoint": "mysql://DB_HOST:3306/DB_NAME",
      "method": "exec",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mysql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create"
      ],
      "resource_id": "mysql-table",
      "description": "Run an allowlisted INSERT against the table. The deployment statement allowlist must hold the exact statement; this registration names the operation and does not supply that control.",
      "requires_review": true
    },
    {
      "capability_id": "mysql.row.update",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:3306"
      ],
      "endpoint": "mysql://DB_HOST:3306/DB_NAME",
      "method": "exec",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mysql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "update"
      ],
      "resource_id": "mysql-table",
      "description": "Run an allowlisted UPDATE against the table, registered at the R2 minimum for update. An overwritten value may be unrecoverable without a backup; assess that separately.",
      "requires_review": true
    },
    {
      "capability_id": "mysql.row.delete",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:3306"
      ],
      "endpoint": "mysql://DB_HOST:3306/DB_NAME",
      "method": "exec",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mysql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "mysql-table",
      "description": "Run an allowlisted DELETE against the table. Delete is one of the designated actions requiring human approval, so the operator approval gate applies.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "mysql-table",
      "kind": "table",
      "locator": "mysql://DB_HOST:3306/DB_NAME/TABLE_NAME",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "draft",
    "date": "2026-10-29",
    "sources": [
      "https://dev.mysql.com/doc/refman/8.4/en/connecting.html (retrieved 2026-10-29)",
      "https://dev.mysql.com/doc/refman/8.4/en/commit.html (retrieved 2026-10-29)",
      "https://dev.mysql.com/doc/refman/8.4/en/set-transaction.html (retrieved 2026-10-29)"
    ],
    "reviewer_id": "aaes-eng"
  }
}
