{
  "document": {
    "version": 6,
    "updated": "2026-10-24",
    "canonical_url": "https://actionlake.com/.well-known/actionlake-trust.json",
    "maintained": "by hand, against the controls in the platform's README",
    "note": "Every field describes a mechanism that exists in the code, or names one that does not. Where a value is null the thing it describes does not exist yet, and `absences` says why. There are no placeholders in this file."
  },
  "service_status": {
    "in_production": true,
    "status_url": null,
    "note": "ActionLake is in production service: paid plans are sold and tenants run real traffic. Every commitment below is an operating position. Where one is not yet in force (the SLA, a status page) its own field says so."
  },
  "surface": {
    "mcp": "https://connect.actionlake.com/mcp",
    "rest": "https://api.actionlake.com/v1",
    "tools": ["help", "find_actions", "get_action", "do", "ask", "check", "cost", "account"],
    "billed_tools": ["do", "ask"],
    "billing_unit": "upstream_call",
    "price_book": "https://api.actionlake.com/v1/price-book",
    "openapi": "https://api.actionlake.com/openapi.json",
    "llms_txt": "https://api.actionlake.com/v1/llms.txt",
    "oauth_metadata": "https://connect.actionlake.com/.well-known/oauth-authorization-server"
  },
  "credentials": {
    "at_rest": "AES-256-GCM under a per-tenant data key, wrapped by a master key (envelope encryption)",
    "per_connection_key": false,
    "aad_bound": true,
    "in_use": "unwrapped in memory for the length of one upstream call; never logged; never returned by any endpoint; never in a prompt or a model's context",
    "agent_holds": "an ActionLake API key, never a vendor token",
    "revocation": "deletion of the connection row and its ciphertext. The data key is the tenant's, not the connection's, so a copy of the row in a database backup stays decryptable until that backup ages out (7 days of point-in-time recovery on managed MySQL) or the tenant is erased",
    "master_key_custody": "an environment key on the API service; not an HSM, not a customer-held KMS",
    "erasure": "erasing a tenant destroys its data key: every credential, trace and spilled cache body sealed under it is unreadable from that moment. A database backup taken before the erasure still holds the wrapped key until it ages out (7 days)",
    "byok": false
  },
  "audit_record": {
    "mechanism": "one row per run in `runs`: tenant, key, action, connection, status, cost, cache verdict, timing; each row stores sha256(prev_hash + row)",
    "seal": "the chain head per tenant is written to object storage once a day",
    "contents": "identifiers, hashes, costs and outcomes - no request or response bodies; those are in `traces`, kept for the plan's window",
    "verifiable_by_customer": "per run - every run page shows prev_hash, hash, input_hash, output_hash and the hashed fields, so a run can be checked against the one before it. The console's CSV export carries the table's columns, not the hashes, and no endpoint yet returns a range of rows for recomputing the chain",
    "external_witnesses": null
  },
  "traces": {
    "stored": "every run's vendor request and response: headers and bodies, connection secrets removed, each body cut at 1 MiB",
    "sealed": "AES-256-GCM under the tenant's data key",
    "where": "a DigitalOcean Spaces bucket in syd1",
    "retention": "the plan's trace window, fixed when the run is recorded: 24 hours on Free, 72 hours on Starter, 7 days on Pro, 14 days on Team, 30 days on Business and Enterprise; then deleted",
    "retention_source": "trace_retention_hours in the price book",
    "readable_by": "the key that made the run, the root key, or a key granted traces:*"
  },
  "response_cache": {
    "stored": "the response body of a cacheable read, keyed by tenant and connection",
    "where": "managed Valkey in syd1; a body over 32 KB is sealed under the tenant's data key and kept in the syd1 bucket instead",
    "retention": "the action's cache TTL, declared in its service file; a write invalidates the reads it affects"
  },
  "personnel_access": {
    "product_screens": "none - no ActionLake screen shows one tenant's credentials, traces or cached bodies to anyone outside that tenant",
    "production": "limited to named ActionLake engineers, for operations; the master key is an environment key on the API service, so those engineers could decrypt customer content"
  },
  "isolation": {
    "tenant_id_on_every_query": true,
    "cache_key_includes_connection_id": true,
    "cross_tenant_access_tested": "in the test suite, on the endpoints; no third-party test yet",
    "another_tenants_record": "answered as if it does not exist"
  },
  "egress": {
    "allowed_hosts": "declared per service file; a request to any other host is refused",
    "private_ranges_refused": true,
    "redirects": "re-checked against the same allow list on every hop",
    "tls_minimum": "declared per service file"
  },
  "dedicated_ip": {
    "offered": true,
    "addresses_per_tenant": 1,
    "shared_with_other_tenants": false,
    "how_to_get": "available on Team and above as an add-on, added to a tenant by ActionLake on request; Enterprise includes one address",
    "min_plan": "team",
    "region": "syd1",
    "fails_closed": true,
    "carries": [
      {"protocol": "https", "ports": ["443", "80", "1024-65535"]},
      {"protocol": "mysql", "ports": ["443", "80", "1024-65535"], "tls": "the database's own, end to end"},
      {"protocol": "postgresql", "ports": ["443", "80", "1024-65535"], "tls": "the database's own, end to end"},
      {"protocol": "redis", "ports": ["443", "80", "1024-65535"], "tls": "the database's own, end to end"}
    ],
    "not_yet": ["plain http", "other ports below 1024"]
  },
  "model_on_data_path": false,
  "training_on_customer_traffic": false,
  "residency": {
    "provider": "DigitalOcean",
    "regions": ["syd1"],
    "single_region": true,
    "data_lands_in": "Australia, at rest",
    "note": "Stored in syd1. In transit via Cloudflare's edge; approval mail via SendGrid (United States). A loss of the region is an outage, not a failover.",
    "in_transit": "through Cloudflare's global edge, which terminates TLS for actionlake.com and the api, mcp, connect and auth hosts"
  },
  "availability": {
    "published_sla_pct": "99.9",
    "applies_to_plans": ["team", "business", "enterprise"],
    "sla_in_force": false,
    "sla_note": "an availability target, not a contractual commitment: no service-credit schedule is in the terms yet",
    "backups": "managed MySQL daily backups with 7 days of point-in-time recovery, kept in the region; a restore to a scratch database is exercised monthly and the chain verified from the seal"
  },
  "certifications": {
    "held": [],
    "soc2": "in progress (Type II)",
    "iso27001": "in progress",
    "penetration_test": "in progress (third-party)",
    "note": "ActionLake holds no security certification yet. SOC 2 Type II, ISO 27001 and a third-party penetration test are in progress. Nothing on this page, on the trust page, or in any ActionLake document should be read as a certification, an attestation, or an auditor's opinion until the report or certificate is issued and listed under `held`."
  },
  "subprocessors": [
    { "name": "DigitalOcean, LLC", "purpose": "compute, managed MySQL, managed Valkey, object storage (traces, seals, spilled cache bodies), the static site", "region": "syd1", "country": "Australia" },
    { "name": "Cloudflare, Inc.", "purpose": "DNS, TLS termination, CDN and WAF for actionlake.com and the api, mcp, connect and auth hosts; sees traffic in transit, including API keys, tokens and credentials posted to connections.create", "region": "global", "country": "United States" },
    { "name": "Twilio Inc. (SendGrid)", "purpose": "approval mail: the approver's address, the request summary, and the action's input with credential-named keys masked", "region": "global", "country": "United States" }
  ],
  "incident_notification_hours": 24,
  "dpa": {
    "summary_url": "https://actionlake.com/legal/dpa",
    "document_url": null,
    "role": "processor"
  },
  "security_txt": "https://actionlake.com/.well-known/security.txt",
  "disclosure": {
    "contact": "mailto:security@actionlake.com",
    "safe_harbour": "Good-faith research that follows this policy will not be met with legal action from ActionLake, and we will say so in writing to anyone who asks.",
    "in_scope": "the MCP and REST surfaces, the OAuth 2.1 server, tenant-isolation testing across two identities you own, the egress guard, and tamper attempts on the run ledger",
    "out_of_scope": [
      "Testing against another customer's data or another customer's connected systems. Ask for a sandbox tenant instead - one unauthenticated call creates one.",
      "Denial of service and volumetric testing.",
      "Social engineering of ActionLake people or ActionLake's vendors.",
      "Findings in a third-party product rather than in ActionLake."
    ],
    "disclosure_window_days": 90,
    "encrypted_channel": null
  },
  "absences": [
    { "field": "service_status.status_url", "reason": "No public status page exists yet.", "closed_by": "publish one at status.actionlake.com, with the cache hit rate and latency split by hit and miss" },
    { "field": "subprocessors", "reason": "Stripe is not listed because no Stripe account is connected: billing.add_card records a token and the plan change, and no card is charged, so no customer data reaches Stripe from ActionLake.", "closed_by": "list Stripe (card tokenisation and invoicing; card details never touch ActionLake), with notice, before the first charge" },
    { "field": "audit_record.external_witnesses", "reason": "The daily seal proves the history to anyone who holds a copy, but it is not yet anchored outside ActionLake's own infrastructure.", "closed_by": "publish the chain head to an independent, append-only location under a separate credential" },
    { "field": "credentials.byok", "reason": "Envelope encryption per tenant under one environment master key is what exists. A customer-held key is not offered.", "closed_by": "a KMS-backed master key, when a customer needs it" },
    { "field": "certifications.held", "reason": "No certification is held yet. SOC 2 Type II, ISO 27001 and a third-party penetration test are in progress.", "closed_by": "complete each one and list the report or certificate under `held`, with its date and scope" },
    { "field": "dpa.document_url", "reason": "https://actionlake.com/legal/dpa describes what the DPA commits us to; the signable document is not published.", "closed_by": "publish the DPA and its technical measures annex" },
    { "field": "disclosure.encrypted_channel", "reason": "No OpenPGP key exists for security@actionlake.com. A published fingerprint for a key nobody holds is worse than none.", "closed_by": "generate the key, publish it, add Encryption to security.txt" },
    { "field": "credentials.per_connection_key", "reason": "Credentials are sealed under the tenant's data key. Deleting a connection deletes its row, but a backup copy of that row stays decryptable until the backup ages out.", "closed_by": "a per-connection data key, wrapped by the tenant's and destroyed with the row" },
    { "field": "audit_record.verifiable_by_customer", "reason": "A run can be checked against its neighbour on its page; the whole chain cannot yet be recomputed by a customer, because no export or endpoint returns a range of rows with their hashes.", "closed_by": "a chain export: every hashed field and both hashes, for a range" }
  ]
}
