Secure by design: deny by default, credentials never stored in the clear, and identity delegated to whatever directory you already run.
Studio opens on a cloud-console-style sign-in page. Email and password works out of the box — the first account created becomes the lake administrator — and Google or Microsoft SSO light up when configured:
export ANYLAKE_GOOGLE_CLIENT_ID=... ANYLAKE_GOOGLE_CLIENT_SECRET=...
export ANYLAKE_MS_CLIENT_ID=... ANYLAKE_MS_CLIENT_SECRET=... ANYLAKE_MS_TENANT=...
anylake studio /data/lake # redirect URIs: /auth/callback/{google,microsoft}Accounts live in the lake (_security/users.json) with PBKDF2-HMAC-SHA256 hashing at 240k iterations and per-user salts; sessions are random 256-bit tokens in HttpOnly, SameSite cookies. SSO uses the OAuth 2.0 authorization-code flow with CSRF state validation.
Machine clients (Grafana, scripts) authenticate with --token / ANYLAKE_TOKEN. --no-accounts disables sign-in entirely for local prototyping.
Serve behind TLS in production — session cookies are not marked Secure, so a plaintext deployment exposes them.
For real organisations, skip local accounts and connect the directory you already have:
from anylake.auth import LdapProvider, OidcProvider, Authorizer
# on-prem Active Directory / any LDAP
idp = LdapProvider("ldaps://ad.corp.com",
"uid={username},ou=people,dc=corp,dc=com",
"ou=groups,dc=corp,dc=com")
# or any cloud entity: Entra ID, Google, Okta, Keycloak, AWS Identity Center
idp = OidcProvider(issuer="https://login.microsoftonline.com/<tenant>/v2.0",
audience="api://anylake")
alice = idp.authenticate(username="alice", password="...") # or token=jwtDeny-by-default RBAC: a table you lack a grant for is never registered in your session, so it doesn't exist for that query — no metadata leakage. Grants live in the lake itself, not in a separate service.
az = Authorizer(lake)
az.grant("group:analysts", "sales*", ["read"])
Engine(lake, principal=alice, authorizer=az).sql("SELECT ...")Data files are immutable with content-addressed names, so there are no overwrite attacks. Transport is TLS throughout (ldaps://, HTTPS object storage, verified JWT signatures). At-rest encryption is delegated to the store's native SSE/KMS/CMEK — on by default, no key juggling.
SQL surfaces are read-only and single-statement: Studio, Grafana, and the MCP query tool reject anything that isn't a lone SELECT.
RBAC is enforced in-process. Anyone holding the underlying storage credentials can read the Parquet files directly, bypassing grants. Closing this needs a mediating server that brokers all access — see the roadmap. Until then, treat storage credentials as the real security boundary and scope them per environment.