Skip to content

Lakekeeper+ Release Notes

v0.14.1 (2026-10-16)

Based on Lakekeeper OSS v0.14.1.

Bug Fixes

  • UI paths. The UI no longer serves a file for a path that contains ui/ a second time. Such a path now returns 404.
  • Security updates of dependencies. rustls, quinn-proto and xxhash-rust are updated to fix security advisories RUSTSEC-2026-0338 to -0342, RUSTSEC-2026-0346 to -0349 and GHSA-6g2r-675j-hx59.

Upstream Lakekeeper changes (bump to v0.14.1)

All changes are in the Lakekeeper release notes. The most important ones for Lakekeeper Plus:

  • Security: S3 remote signing signs only what it knows. Unknown query parameters and headers are refused, and so are writes that use an encryption key other than the warehouse's (lakekeeper#2102).
  • Remote signing with Iceberg 1.12 or later. Batch deletes and repeated deletes, for example in expire_snapshots or remove_orphan_files, no longer fail with 403 SignatureDoesNotMatch (lakekeeper#2124, lakekeeper#2104).
  • Concurrent commits to one table that block each other in the database return a 409 error, which clients retry, instead of 503. A commit that removes the last branch or tag of a table is now kept (lakekeeper#2117).

v0.14.0 (2026-10-08)

Based on Lakekeeper OSS v0.14.0.

Highlights

  • Cedar: grants decide access. Predefined policies turn the grants you give in the console or through the Grants API into access, so you can hand out permissions without changing a policy file. Each project and warehouse chooses which predefined policies apply. See Predefined policies.
  • Cedar: policies per project and warehouse. Each project and warehouse can keep its own Cedar policies, managed in the console or through the API. If a policy locks everyone out, an operator can repair it with break-glass. See Break-Glass.
  • Cedar: roles and governance tags. Roles can be created and managed in Lakekeeper, and policies can decide access by governance tags. See Tag-Based Access Control.

Features

  • Grants in policies. Each resource carries what the principal holds on it as principal_privileges: direct for grants on the resource itself, inherited for grants above it. For example: resource.principal_privileges.direct.select. Grant actions carry the privilege being granted as context.privilege. With externally managed users and roles, give grants to users: your entity file holds role membership, so a grant to a role does not apply.
  • Predefined policies. They are on by default; LAKEKEEPER__CEDAR__PREDEFINED_POLICIES_ENABLED=false switches all of them off. A project or warehouse can switch single policies on or off. A policy that a later release adds starts switched off where you have already chosen. With include-policy=true, the listing also returns each policy's rule. Policy listings say in can-write and can-toggle whether you may change them.
  • Policies per project and warehouse. Manage them in the console or through the …/policies endpoints. A change is checked as a whole and applied in one transaction. dry-run previews it, if-scope-version protects against concurrent edits, and replace: true replaces all policies of the scope. A scope holds up to 1000 policies (LAKEKEEPER__CEDAR__MAX_POLICIES_PER_SCOPE).
  • Break-glass. With the header x-break-glass: <reason>, an operator is judged by the server's policies alone and can repair a project's or warehouse's policies. It opens policy administration only, never data, and every use is audited with its reason. The break-glass-status endpoint tells you whether it is open to you. Instance admins must send the header to write a scope's stored policies.
  • New privileges for separation of duties. manage_policies reads and writes a scope's policies, and read_policies only reads them. read_grants shows who holds what on an object and below it; this reveals that an object exists, even where a policy hides it. manage_tags tags objects; attaching a tag also needs apply or manage on the tag, so hand out apply carefully when policies decide access by tags. None of these privileges gives access to data by itself, but a manage_policies holder can write a policy that does, so manage_policies is never part of manage. pass_grants passes on only describe, select, write, create and, on a tag, apply.
  • Governance tags in policies. Creating, applying, removing, reading, changing and deleting tags are Cedar actions. Each warehouse, namespace, table, view and generic table carries its effective governance tags, including inherited ones, as governance_tags. For example: resource.governance_tags.hasTag("pii"). Keys match the tag name exactly, including case. Column tags are not included.
  • Grant administration for whole subtrees. Listing and revoking all grants under a warehouse or namespace, and listing everything one user or role holds in a project, are Cedar actions of their own. By default, manage_grants and read_grants holders may list, and manage_grants holders may revoke. Policies see what a request covers in context.subtree. The decision is made once, on the named warehouse or namespace; a forbid on objects below does not narrow it. A bulk revoke cannot be undone.
  • Roles under Cedar. Roles managed in Lakekeeper (provider lakekeeper) can be created, renamed, given members and deleted, in the console and through the API. By default, project manage_grants holders manage them, and describe, create, manage and manage_grants holders can read them. Roles from identity and role providers can be deleted too, for example after their group was removed from the directory. A role that still holds grants needs force=true.
  • Moves are checked at both ends. Moving a table, view or generic table into another namespace checks MoveTable, MoveView or MoveGenericTable on the object and AcceptMovedTabularInNamespace on the new namespace. Policies see the new namespace in context.destination and the old one in context.source. New predefined policies allow moves, including moving and renaming namespaces: manage and manage_grants on the object to move it out, and create or manage on the new namespace or warehouse to move it in.
  • Server grants. Grants on the server go to users; a grant to a role is refused with 400 ServerGrantToRole. No predefined policy decides server actions: load the five server-grant permits from the "Server Actions" section of the schema to make server grants take effect. See Server grants.
  • Server policy check. When the server loads its policies, it refuses a policy that would stop fewer users at a server action, and the log shows the fix. The check does not run when users and roles are managed externally.
  • Batch checks name the deciding policies. Under Cedar, each result of POST /management/v1/action/batch-check lists the policies that decided it in determined-by.
  • Server policy endpoints renamed. policy-sources and policy-list are now server-policy-sources and server-policy-list. The old paths still work but are deprecated. Each source now reports its tier.
  • Console v0.23.0. Each project's and warehouse's Cedar policies and predefined policies can be managed in the console. With Cedar, grants under a warehouse or namespace can be listed and revoked in bulk. On a table's Tasks tab, snapshot expiration and orphan-file removal each have "Run now" and settings buttons. The console also includes everything in the Lakekeeper console v0.26.0.
  • Console settings. LAKEKEEPER__UI__STORAGE_PROVIDERS_ORDER and LAKEKEEPER__UI__STORAGE_PROVIDERS_HIDDEN order and hide the storage types offered when adding a warehouse. LAKEKEEPER__UI__SUPPORT_DOCS_URL, LAKEKEEPER__UI__SUPPORT_ISSUE_URL and LAKEKEEPER__UI__SUPPORT_CONTACT_URL set the links of the help menu.
  • Admission gate metrics. lakekeeper_admission_enforce_call_duration_seconds, lakekeeper_admission_enforce_decisions_total and lakekeeper_admission_enforce_fail_closed_total show how the gate performs. Its cache reports as cache_type="admission_enforce".
  • New commands. openfga reconcile repairs OpenFGA's hierarchy tuples from the catalog; OpenFGA users can now run the reconcile recommended in 0.13.6. reopen-bootstrap --yes allows bootstrapping again, for example after a misconfigured first bootstrap. It changes no data and no policies, but until bootstrap runs again, the next authenticated caller of /management/v1/bootstrap becomes the initial admin.

Bug Fixes

  • Role providers: stored roles are used only when every unreachable provider has synced the user. Before, a policy that forbids a group from a missing provider could be skipped.
  • Role providers: fewer directory calls. A user's groups are fetched once and reused in every project until they are due for a sync. A user without groups keeps working while the provider is down.
  • Persisted token roles apply in every project. While a user is active, their token roles are stored again at most every two minutes. A decision about a user who has made no request in the project, such as a DEFINER view's owner, uses their newest stored roles from any project.
  • Cedar: the project list decides each project with the caller's roles in that project. Acting as a role lists only that role's project.
  • Looking up another user's groups writes nothing. Permission checks about another user and DEFINER views no longer create users or roles. A caller who may not make such a check is refused before that user's groups are looked up.
  • Directory role providers resolve only the users of their identity provider. Before, with several identity providers, a user could receive the groups of another provider's user with the same subject.
  • Roles named after an identity provider that supplies no roles can be renamed and deleted. managed-role-providers in GET /management/v1/info lists only the configured role providers.
  • Snapshot expiration deletes only files inside the table location. Other files are left in place and counted in the task result's skipped-outside-table-location-count, so tables that write data outside their location, for example with write.data.path, keep their expired data files. A snapshot history that loops no longer hangs expiration, and a branch or tag moved onto an expiring snapshot during expiration is kept.
  • Commands behave as in Lakekeeper. healthcheck without flags checks /health (before, it checked nothing), wait-for-db reads the migration state from the primary database, migrate runs the steps after the migration, and serve reports the lakekeeper_catalog_pg_pool_* metrics.
  • Cedar: a request the authorizer cannot build returns 500 instead of 503. Retrying does not help; the details are in the server log.

Breaking Changes

  • Cedar: server actions use the user's groups. Server actions are the few actions on the server itself, such as creating projects and managing users. At server actions, policies see the user's groups from Entra ID, LDAP, Okta, token claims and admission gates, and these groups are the same in every project. Roles managed in Lakekeeper belong to one project, so they do not count at server actions. In server-action policies, name a group with principal.project_roles.contains({provider_id: "ldap", source_id: "<group>"}) instead of a Lakekeeper::Role::"…" id. Send server requests without x-assume-role, because Lakekeeper denies server actions to requests with x-assume-role. See Role scope at server actions.
  • Cedar: new action groups for reading policies and for checks about other users. Reading the server's policy sources is in ServerCedarPolicyActions and CedarPolicyReadActions. Checking what another user or role may do (Introspect<X>Authorization) is in <X>GrantActions and GrantReadActions. If your policies allow these actions through ServerActions, <X>Actions, <X>ModifyActions or RoleActions, name the new groups.
  • Cedar: allow reading role assignments on the project. ReadUserRoleAssignments is now decided on the project and is in no action group. To let users read the role assignments of other users, permit ReadUserRoleAssignments on the project in your own policies, or grant these users describe, create, manage or manage_grants on the project. A policy that names ReadUserRoleAssignments on the server stops the server from starting.
  • Cedar: requests are decided in the project of their resources. A catalog request without x-project-id uses the project of its warehouse, so Iceberg clients need no x-project-id header. A management request without x-project-id uses the default project. Lakekeeper refuses a request whose x-project-id names a different project than its resources. With more than one project, make sure that management clients send the x-project-id of the resources they manage.
  • Cedar: moves into another namespace have their own actions. Moving a table, view or generic table into another namespace needs MoveTable, MoveView or MoveGenericTable on the object and AcceptMovedTabularInNamespace on the new namespace, in addition to the rename and create actions. No action group includes these four actions, so name them in your policies. Renames inside one namespace need no change.
  • Cedar: three requests check one more action. Make sure your policies allow the new action:
    • The project list shows the projects where IncludeProjectInList is allowed. Before, the project list checked GetProjectMetadata.
    • Registering a table also checks GetNamespaceMetadata on the namespace.
    • Cancelling a soft-deletion task also checks UndropTable, UndropView or UndropGenericTable, because cancelling the task restores the object.
  • Persisted token roles follow the newest token. With LAKEKEEPER__ROLE_PROVIDER_CHAIN__PERSIST_TOKEN_ROLES=true, Lakekeeper replaces a user's stored token roles with the roles of their newest token, also when the newest token carries no roles. Entra ID leaves out the groups claim for users in more than 200 groups, so give these users their groups through the Entra ID role provider before you upgrade.
  • Check your configuration before you upgrade. The server checks more settings at startup and stops with a message that names the setting:
    • LAKEKEEPER__AUTHZ_BACKEND must be allowall, openfga or cedar. Other values, such as a typo, used to start the server without authorization.
    • An authenticator must be configured, such as OpenID or Kubernetes authentication. To run without authentication, set LAKEKEEPER__INSECURE_ALLOW_UNAUTHENTICATED=true.
    • With Cedar and users from two or more identity providers, every LDAP, Entra ID and Okta role provider must set idp_ids. Kubernetes authentication counts as an identity provider. Every entry in idp_ids must name an identity provider that the server authenticates.
    • An admission gate authenticates to your endpoint with headers. The auth setting is removed, so the gate never sends the caller's token. The gate configuration must not contain unknown keys, and its idp_id must name an identity provider that the server authenticates.
  • Audit log format. Existing parsers of the audit log must be updated. See Audit log format below.

Upgrade Notes

  • Plan downtime and back up the database. Stop all 0.13 servers; with Helm, scale the deployment to zero before helm upgrade. With OpenFGA, remove LAKEKEEPER__OPENFGA__AUTHORIZATION_MODEL_VERSION. Run migrate to completion, then start 0.14. The migration can take several minutes on large catalogs. First run the query from "Tables or views that would share a location stop the migration" in the Lakekeeper upgrade notes. Afterwards, 0.13 servers cannot run against the database; to go back, restore the backup.
  • Grants decide access as soon as they are made. The predefined policies are on by default. To choose them per project or warehouse before they apply, start with LAKEKEEPER__CEDAR__PREDEFINED_POLICIES_ENABLED=false, switch off what you don't want in each scope, then set it back to true.
  • Bulk revoke is on by default. manage_grants holders can revoke all grants under a warehouse or namespace, their own included, and a revoke cannot be undone; break-glass does not restore grants. Switch off predefined-grants-warehouse-grant-admin-subtree-revoke and predefined-grants-namespace-grant-admin-subtree-revoke where you don't want this.
  • Review policies that allow role actions. With Cedar, version 0.13 refused to create or delete roles, whatever the policies allowed. Version 0.14 follows the policies: a policy that allows CreateRole lets its holder create roles, and a policy that allows DeleteRole, also through RoleActions or through all actions, lets its holder delete roles, including roles from Entra ID, LDAP and Okta.
  • Directory outages: let service accounts make a request in a project now and then. While an LDAP, Entra ID or Okta directory is down, Lakekeeper uses the groups it stored at the user's own requests in a project. A user who has never made a request in a project, for example a provisioning bot, gets an error while the directory is down, and so does a query on a DEFINER view that this user owns.
  • OPA bridge: if you use it, deploy its policies from this release together with the server. Renames into another schema now also check move and accept_moved_tabular.

Upstream Lakekeeper changes (up to Lakekeeper v0.14.0)

This release includes Lakekeeper v0.13.6 and v0.14.0. See the Lakekeeper release notes for all changes. Most important for Plus:

  • Grants API, governance tags, and moving and renaming namespaces (lakekeeper#1945, lakekeeper#1914, lakekeeper#1950).
  • Storage validation, with warnings for STACKIT buckets that other credentials groups can reach and for CORS settings that block the console (lakekeeper#1936, lakekeeper#2050, lakekeeper#2049).
  • STACKIT storage type with data platform storage, and Alibaba Cloud OSS (lakekeeper#1978, lakekeeper#2045, lakekeeper#1894).
  • Audit log format changes, listed in full under Audit log format below.
  • Required token claims, and stricter OpenID settings that can stop the server from starting (lakekeeper#2001).
  • Task queue renamed. Rename LAKEKEEPER__TASK_TABULAR_EXPIRATION_WORKERS to LAKEKEEPER__TASK_SOFT_DELETION_WORKERS before you upgrade; the old name stops the server from starting (lakekeeper#1881).
  • Requests may be up to 32 MiB by default (lakekeeper#1974).
  • Role members cache removed. The LAKEKEEPER__CACHE__ROLE_MEMBERS__* settings have no effect (lakekeeper#2072).
  • Catalog requests without x-project-id use their warehouse's project, and token roles work without a project. Under Cedar, token and admission-gate roles are in every project's principal.project_roles (lakekeeper#2069, lakekeeper#2071).
  • OpenFGA: migrate installs model v4.13. Revoking needs manage_grants. Moving a table, view or generic table into another namespace needs manage_grants and modify on it. Moves and managed-access changes are not possible under an assumed role. Tools that query OpenFGA directly must use the new _effective relations, such as select_effective (lakekeeper#1991, lakekeeper#2029, lakekeeper#2095).
  • Cancelling a soft-deletion task needs undrop with every authorizer (lakekeeper#2095).
  • Batch check: update_storage_credential is removed and returns 422; check update_storage instead (lakekeeper#1936).
  • Smaller changes that can affect clients: reusing an Idempotency-Key for a different operation returns 400, referenced-by names at most 10 views by default, tables can no longer set write.object-storage.path or write.folder-storage.path, and unreachable storage fails with 412 StorageProbeTimeout (lakekeeper#1959, lakekeeper#1980, lakekeeper#2044, lakekeeper#2047).
  • All fixes of Lakekeeper v0.13.6, which Plus v0.13.6 did not include yet.

Audit log format

Lakekeeper Plus writes the same audit log as Lakekeeper, so this section lists the audit log changes of Lakekeeper v0.14.0 together with those of Lakekeeper Plus.

Breaking changes — an existing parser must be updated:

  • A field is absent when it has no value, and an empty list or map is written out. Nothing in an audit record is null.

    A field the request did not supply is left out. A policy factor without a name, for example, has no name key instead of "name": null.

    An empty list or map is a value and is always written once an action can carry it:

    before  {"action_name": "commit"}
    after   {"action_name": "commit", "updated_properties": {}, "removed_properties": [], "target_refs": [], "update_kinds": []}
    

    This applies to properties, updated_properties, removed_properties, source, destination, target_refs and update_kinds in an action, and to authorizations[].determined_by and error.stack. A flag such as force, purge or recursive is also always written, as true or false.

    What to do: read "none" from an empty value, not from a missing key. Keys that are genuinely optional, such as a name or an id, still need a presence check.

  • Authorization records always carry actions and entities as lists. The singular action and entity fields are gone.

    before  "action": {…}            or  "actions": [{…}, {…}]
    after   "actions": [{…}]
    
    before  .entity.namespace         .action.action_name
    after   .entities[0].namespace    .actions[0].action_name
    

    Each entry of authorizations[] still carries a singular action and entity.

    What to do: read actions and entities as lists, and iterate: a record can carry more than one.

  • A context value has its own JSON type. It is no longer always a string.

    before  "force": "true"   "dry_run": "true"   "writes": "2"
    after   "force": true     "dry_run": false    "writes": 2
    
    • Booleans: force, purge, recursive, dry_run and allow_partial in an action, and self_provisioning and self_read in context.
    • Numbers: writes and deletes.
    • Principals: principals on apply_grants and principal on read_subtree_grants and revoke_subtree_grants are {"user": …} or {"role": …}, the form context.principal has on grant records. A subtree request states with principal_scope (every or one) whether it names one principal; principal is present only for one.
    • Strings, lists and maps keep their type.

    Lakekeeper Plus adds context keys that hold an object, such as scope_changes and applied on apply_cedar_policies. schema-plus.json gives the object's definition.

    What to do: compare a flag with true, not with "true". Read each key's type from the schema.

  • The entries of authorizations[].determined_by have the shape the management API returns from a permission check, and there are two new kinds.

    before  {"Policy": {"policy_id": "p-42", "effect": {"Permit": []}, "source": "cedar"}}
    after   {"type": "policy", "policy-id": "p-42", "effect": "permit", "source": "cedar"}
    
    • The kind is in type. Field names are kebab-case, as in the API. effect is permit or forbid.
    • {"type": "system-authority"}: a built-in authority, not a configured policy, decided the allow. It may carry source and reason.
    • {"type": "admission-gate"}: an admission gate would refuse the user. It carries gate, and check when the gate names one.

    What to do: switch on type. The same code can parse these entries and a /check response.

  • A request that names nothing to check records empty lists. The made-up unknown entry is gone.

    A batch check with checks: [], and a transaction commit with no table changes, recorded one authorizations entry with entity_type unknown, and for the commit action_name unknown too.

    before  "authorizations": [{"action": {"action_name": "unknown"}, "entity": {"entity_type": "unknown"}, …}]
    after   "actions": [], "entities": [], "authorizations": []
    

    decision still carries the outcome.

    What to do: stop matching unknown in entity_type and action_name, and handle an empty authorizations list.

  • Every key and every value this log names itself is spelled snake_case, and failure_reason is a plain string.

    Keys that carried a hyphen now carry an underscore:

    • in an entity: generic_table, generic_table_id, namespace_id, project_id, role_id, role_provider_id, role_source_id, server_id, table_id, table_location, tag_definition_id, task_id, user_id, view_id, warehouse_id
    • in an action: allow_partial, created_before, dry_run, removed_properties, target_refs, update_kinds, updated_properties
    • in an authorization record's context: invoked_by, self_provisioning, self_read
    • on an authorizations[] entry: for_principal

    Values:

    actor_type      before  assumed-role                after  assumed_role
                    before  lakekeeper-internal         after  lakekeeper_internal
    failure_reason  before  {"ActionForbidden": []}     after  "action_forbidden"
    

    Every failure_reason value follows the same pattern: action_forbidden, resource_not_found, cannot_see_resource, internal_authorization_error, internal_catalog_error, invalid_request_data.

    Three sets keep the spelling of the vocabulary they come from: entity_type and resource_type (generic-table, tag-definition, as in the management API) and update_kinds (Iceberg's update names, such as add-schema). The objects in determined_by keep the management API's field names, policy-id included.

    What to do: rename these keys and values in every query. A query on the old spelling matches nothing and raises no error, so check for the new names explicitly. In jq, .failure_reason | keys[0] becomes .failure_reason.

  • A role on a record carries its provider_id and source_id next to its id. provider_id is always shown; LAKEKEEPER__AUDIT__TRACING__INCLUDE_ROLE_SOURCE_ID=false leaves source_id out.

    before  {"role": "1f7b…"}
    after   {"role": "1f7b…", "provider_id": "corporate-ldap", "source_id": "engineering"}
    

    This applies wherever a role is named: authorizations[].for_principal, context.principal on grant_created and grant_revoked, principals on apply_grants, and principal on read_subtree_grants and revoke_subtree_grants. Both fields are absent when the role no longer exists or could not be looked up. With the setting off, source_id is absent everywhere, including on actor.assumed_role, which carries it otherwise.

    What to do: correlate on the role's id. Read provider_id and source_id as optional on a role a record names, and source_id as optional on actor.assumed_role. Some providers let a source id be a free-form name, so it might hold personal data; turn the setting off if that matters for your log.

  • Renaming a table, view or generic table into another namespace is recorded as move, not rename, and namespaces have a new action accept_moved_tabular.

    renameTable, renameView, renameGenericTable into another namespace
      before   rename
      after    move   destination
    namespace  accept_moved_tabular   source
    

    The record of such a rename names move on the renamed entity, refused or allowed, on replays as well. destination holds the path of the destination namespace; the entity's new name is not part of it, unlike for a namespace move, where destination is the full new path. A rename within one namespace is still recorded as rename. Whether the namespace changes is decided on the paths in the request, ignoring upper and lower case of ASCII letters: two paths that differ only in the case of a non-ASCII letter are recorded as move.

    accept_moved_tabular is the check on the destination namespace. source holds the path of the namespace the entity is moved from. It appears in records of permission checks that name it, such as /management/v1/action/batch-check.

    The record of cancelling soft-deletion tasks is unchanged: it still names control_tasks. Cancelling such a task now also requires undrop on its table, view or generic table, and a refusal is recorded as a denied control_tasks.

    What to do: if you match table, view or generic-table renames on rename, match move as well.

  • context.roles on resolve_roles records with outcome: "roles_resolved" lists role objects instead of strings.

    before  "roles": ["corporate-ldap~engineering", "oidc~viewers"]
    after   "roles": [{"role": "1f7b…", "provider_id": "corporate-ldap", "source_id": "engineering"}, {"provider_id": "oidc", "source_id": "viewers"}]
    

    Each object has the keys Lakekeeper's records name a role with. role is the role's catalog id, absent for a role without a catalog row for this answer: token roles, and roles resolved above projects. source_id is absent when LAKEKEEPER__AUDIT__TRACING__INCLUDE_ROLE_SOURCE_ID=false. The list stays ordered by provider and source id.

    What to do: read each entry of roles as an object. To rebuild the old string, join provider_id and source_id with ~.

Additions — an existing parser keeps working:

  • Authorization records carry three new optional fields about the request: user_agent, break_glass and idempotency_key.

    • user_agent: the request's User-Agent header, as sent and unverified.
    • break_glass: the reason the caller stated in the x-break-glass header, as sent and unverified.
    • idempotency_key: the request's Idempotency-Key.

    Each is absent when the request did not send it.

    What to do: nothing. Treat user_agent and break_glass as claims, never as identity.

  • Three more operations record the override that makes them destructive.

    role delete          force   revokes the grants the role still holds
    warehouse delete     force   deletes a protected warehouse and everything in it
    generic-table drop   force   skips the warehouse's soft-deletion window
                         purge   deletes the data files
    

    They use the same keys as namespace delete, table drop and view drop. The key is present whichever way the flag went.

    What to do: if you alert on destructive operations, include these three. "force": true is the forced form.

  • Three new kinds of audit record: grant changes, admission decisions and idempotent replays.

    • operation: "grant_created" and "grant_revoked": one record per grant the backend reports as applied, with outcome: "success" and context principal, privilege, resource_type, and resource_id and warehouse_id where they apply.
    • operation: "admission_decided": a request an admission gate refused. outcome is forbidden or unavailable; context carries gate, status, error_type, message, error_id, and denied_by when the gate named a rule.
    • record_type: "replay": a request answered from an idempotency record without being executed. It carries the actions and entities the request named and the idempotency_key that matched, and no decision.

    What to do: nothing, unless you want these records. Select them by operation or by record_type.

  • Records can carry the email of the users they name. Off by default; LAKEKEEPER__AUDIT__TRACING__INCLUDE_USER_EMAIL=true turns it on.

    actor.email                          the principal's email, for principal and assumed_role actors
    authorizations[].for_principal.email  the user's email, for a user subject
    actions[].principals[].email         the user's email, for a user an apply_grants request names
    actions[].principal.email            the user's email, when a subtree grant request names one user
    context.principal.email              the recipient's email, on grant_created and grant_revoked
    

    Each is best-effort: absent when the email is not known, never null. Roles, anonymous and lakekeeper_internal actors never carry one. An email is metadata, not identity: emails are not unique and can change.

    What to do: correlate on principal, user and role, never on email. If you enable the setting, treat the log as holding personal data: an email stays in the log after the user is deleted.

  • Audit records carry four new top-level fields: record_type, emitters, time, and request_id when a request caused the record.

    "record_type": "authorization",
    "emitters": {"lakekeeper": "1.0"},
    "request_id": "019684ff-…",
    "time": "2026-03-14T09:26:53.589793Z"
    
    • record_type names the record's shape: authorization, replay or operation.
    • emitters names every product that contributed to the record. Each key is a product name, each value the MAJOR.MINOR version of what that product contributes. Records that Lakekeeper Plus writes itself, such as resolve_roles, carry "emitters": {"lakekeeper_plus": "1.0"}. An authorization record also has the key lakekeeper_plus when it carries one of Lakekeeper Plus's action names or context keys.
    • request_id is the request that caused the record: the value of the x-request-id response header, as sent by the caller or generated by Lakekeeper. It is absent from an operation record no request caused, such as the role-provider records resolve_roles, ldap_resolve_roles and cached_role_provider.
    • time is when the event happened, in UTC, as RFC 3339 with microseconds.

    audit_format governs the record's overall shape. A product's value in emitters governs what that product contributes. The two are equal on a record only Lakekeeper produced; do not rely on that.

    What to do: route on record_type. Read a product's version from its key: .emitters.lakekeeper for Lakekeeper, .emitters.lakekeeper_plus for Lakekeeper Plus. Correlate records with a request through request_id, and order them by time.

  • A new record: operation: "admission_enforce_check" with outcome: "role_withheld".

    The external-enforce gate writes it when the control plane refuses one role but still admits the request. context carries gate, check, role and cache_ttl_secs. role names the admission role by provider_id and source_id, as other records name a role; source_id is absent when LAKEKEEPER__AUDIT__TRACING__INCLUDE_ROLE_SOURCE_ID=false. The gate caches each answer, so there is one record per answer, not per request.

    What to do: nothing, unless you want to see withheld roles. Join it with Lakekeeper's admission_decided records on actor or request_id.

  • Authorization records of Cedar policy requests carry what the request asked to change and how it ended.

    New context keys:

    • changes on toggle_predefined_policy: the predefined-policy switches the request asked for, as {"changes": [{"id": …, "enabled": …}], "truncated": …}.
    • On apply_cedar_policies:
    • dry_run: true when the call only reports what it would do.
    • break_glass: the reason the caller stated, when it stated one.
    • scope_changes: the writes and deletes the request asked for, with writes, deletes, replace, names and truncated.
    • applied: what the apply left behind, with created, updated, deleted, deleted_names, truncated and scope-version.
    • apply_outcome: refused or failed, absent when the apply went through. apply_error says why.

    A list is cut at a fixed length, and truncated is true when it was.

    What to do: nothing, unless you audit policy changes. Check truncated before treating a list as complete.

Also worth knowing — the format itself did not change:

  • Audit records are emitted on the fixed tracing target lakekeeper::audit.

    A log filter naming lakekeeper::service::events::backends::audit, lakekeeper::service::admission or a prefix of either below lakekeeper matches no audit record. The server warns at start-up when it finds such a filter.

    What to do: select audit records with RUST_LOG=warn,lakekeeper::audit=info, or suppress them with RUST_LOG=info,lakekeeper::audit=warn.

  • The audit log has published JSON Schemas: audit/schema.json for Lakekeeper and audit/schema-plus.json for Lakekeeper Plus, in the documentation, linked from the "Audit Log Schema" page.

    • Point a validator at the document to check a whole record. Its root, AuditRecord, requires event_source, audit_format and record_type and selects the shape by record_type: AuthorizationRecord, ReplayRecord or OperationRecord.
    • Every field and key is a property with its type and description. ActionRecord lists, per action_name, the keys that action carries.
    • A value set the log may extend lists its values under x-audit-values, so a validator accepts a value a later release adds. The closed sets decision, privilege_scope, root_level and a policy's effect use enum.
    • schema-plus.json lists Lakekeeper Plus's operation and outcome values, the context of each of its records, the context keys it adds to authorization records, and its action names. A record whose emitters has the key lakekeeper_plus is governed by both schemas.

    What to do: nothing. Validate with both schemas, or generate a parser from them.

Records from v0.14.0 carry audit_format 1.0 (the first version). Records from v0.14.0 that Lakekeeper Plus contributes to carry emitters.lakekeeper_plus 1.0 (the first version).

v0.13.7 (2026-10-16)

Based on Lakekeeper OSS v0.13.7.

Bug Fixes

  • Security: snapshot expiration deletes only files inside the table location. It runs with the warehouse's storage credentials and follows references in table metadata that clients write. A principal that can commit to one table can no longer get files of other tables deleted. A snapshot whose parent chain loops back on itself no longer stops the expiration.
  • Snapshot expiration keeps branches and tags that move during it. When a branch or tag is moved onto a snapshot that the expiration is about to remove, the expiration retries and keeps the snapshot and its files. Before, it dropped the branch or tag.
  • Security: Okta and Entra ID role providers refuse subjects that consist only of dots. Such a subject would change the request URL, so that a lookup of the user's groups could return other groups. The request is refused before it is sent.
  • The health check probes the server by default. lakekeeper-plus healthcheck without flags now checks the server's /health endpoint.
  • Database pool metrics. The server reports the lakekeeper_catalog_pg_pool_* metrics, as Lakekeeper does.
  • migrate sets up the background tasks. lakekeeper-plus migrate now also sets up the scheduled background tasks of Lakekeeper Plus.
  • UI paths. The UI no longer serves a file for a path that contains ui/ a second time. Such a path now returns 404.
  • Security updates of dependencies. rustls and quinn-proto are updated to fix security advisories RUSTSEC-2026-0338 to -0342 and RUSTSEC-2026-0346 to -0349.

Upgrade Notes

  • Snapshot expiration leaves files outside the table location in place. This includes the data files of tables that still carry a write.data.path, write.object-storage.path or write.folder-storage.path property. The task result counts them in skipped-outside-table-location-count.
  • An older binary refuses to start against a newer database. wait-for-db and the server start now stop with an error when the database was migrated by a newer Lakekeeper Plus. wait-for-db reads the migration state from the primary database, not from a read replica.
  • migrate writes structured logs. Its output moves from plain text on stdout to the server's log format.

Upstream Lakekeeper changes (bump to v0.13.7)

All changes are in the Lakekeeper release notes of v0.13.6 and v0.13.7. The most important ones for Lakekeeper Plus:

  • Security: S3 remote signing and metadata cleanup stay inside the table. The signer refuses unknown query parameters and headers, and writes that use an encryption key other than the warehouse's. A commit deletes expired metadata files only inside the table location (lakekeeper#2102).
  • Remote signing with Iceberg 1.12 or later. Batch deletes, repeated deletes and repeated ranged reads no longer fail with 403 SignatureDoesNotMatch (lakekeeper#2124, lakekeeper#2104).
  • Tables can no longer set write.object-storage.path or write.folder-storage.path. These old names of write.data.path make engines write data files outside the table location. Tables that already have them can remove them (lakekeeper#2044).
  • OPA bridge: deploy the updated policies from authz/opa-bridge/policies to get the fix for warehouse IDs that could not be found (lakekeeper#2052).
  • Shared HTTP caches can no longer give one user's response, which may contain vended credentials, to another user (lakekeeper#1947).
  • Connections to ADLS, S3 and GCS time out after 10 seconds, and ADLS reuses connections (lakekeeper#2022).

v0.13.6 (2026-09-18)

Based on Lakekeeper OSS v0.13.5.

Bug Fixes

  • Okta role provider: people who sign in with an email address now resolve to their groups. The user identifier was escaped for a query string rather than for a URL path, so the @ in a login like alice@example.com reached Okta as %40 and every such lookup came back as a bare 400. Deployments whose OIDC subject is a raw Okta user id were unaffected, which is why service principals kept working while people did not.
  • Okta role provider: a retried token request no longer replays its client assertion. Okta accepts each assertion once, so a DPoP nonce challenge or a throttled token request could fail with invalid_client. Every attempt is now signed afresh.
  • Security: TLS handshake handling (RUSTSEC-2026-0285). rustls is updated to 0.23.45, which rejects handshake messages spanning a key change instead of accepting them. The handshake transcript stayed authenticated, so this could not be used to alter or complete a handshake.

Upgrade Notes

  • OpenFGA deployments that have renamed a table, view or generic table across namespaces should run lakekeeper openfga reconcile --mode add-and-delete-drift once after upgrading. The upstream fix below stops the leak but cannot remove a permission edge already recorded, and the default add-missing mode does not repair it.

Upstream Lakekeeper changes (bump to v0.13.5)

  • GovCloud, China and ISO region support. Vended-credential policies now carry the correct ARN partition, derived automatically from the region, endpoint or role ARN, so AssumeRole succeeds for buckets outside the commercial partition (lakekeeper#1928).
  • S3 request signing for generic tables (lakekeeper#1910).
  • Fixed a permissions leak: a table, view or generic table renamed into another namespace kept inheriting grants from the namespace it left (lakekeeper#2013).
  • Renaming a table onto a name that is already taken now returns 409 Conflict instead of 404 Not Found, matching the Iceberg REST spec, and renaming onto a soft-deleted name succeeds (lakekeeper#1955).

v0.13.5 (2026-08-26)

Based on Lakekeeper OSS v0.13.3.

Highlights

  • Improved memory behaviour on long-running instances. Conditions that could, under some circumstances, prevent freed memory from being returned to the OS are addressed.

Features

  • New memory metrics. lakekeeper_jemalloc_* separates live heap from memory the allocator is holding back; lakekeeper_http_connections reports open connections.
  • The table maintenance cache reports what it holds: lakekeeper_cache_weighted_bytes{cache_type="table_metadata"} and lakekeeper_cache_instances, with hits and misses in the shared lakekeeper_cache_* series.

Bug Fixes

  • Transparent huge pages could prevent freed memory from being returned to the OS. On nodes with THP=always, the default on common EKS AMIs, resident memory could grow for the life of the process.
  • The manifest cache under-counted its entries, so under some circumstances its 128 MiB budget did not bind during table maintenance.
  • The UI asset cache keyed on an unvalidated header, so variants of x-forwarded-prefix could each add an entry to a cache with no expiry.
  • Allocator metrics now report on every serving path, including LAKEKEEPER__DEBUG__AUTO_SERVE. Memory behaviour itself was unaffected.

Upgrade Notes

  • Idle HTTP connections now close after 75 seconds and carry TCP keepalive probes. Standard Iceberg and S3 clients retry; previously a connection whose peer had vanished could be held for the life of the process.
  • Request headers above 64 KiB are rejected with 431. Request bodies are unaffected.
  • Shutdown drains for at most 10 seconds before abandoning connections still open.
  • Unmatched request paths report endpoint="unmatched" in metrics instead of each creating a permanent series. Dashboards that group by raw path lose those values.

v0.13.4 (2026-08-14)

Based on Lakekeeper OSS v0.13.3.

Bug Fixes

  • Audit log fields are emitted as structured JSON. actor, action/actions, entity/entities, authorizations and context were emitted as strings containing escaped pseudo-JSON, so audit pipelines could not read them as objects without decoding each field first. They are now nested JSON objects, as documented. See Upgrade Notes.
  • Reduced memory growth from allocator fragmentation. The server now uses jemalloc as its global allocator. Deployments that saw container_memory_working_set_bytes climb steadily without returning to baseline — a glibc malloc fragmentation pattern — should see flatter memory use. The effect depends on workload, and this changes the allocator only.
  • JSON logs no longer carry duplicate span data. Every line included both a span object and a spans array with the same content; only spans is emitted now. Applies to lakekeeper-plus and lakekeeper-maintenance.
  • LAKEKEEPER__DEBUG__LOG_AUTHORIZATION_HEADER now applies to UI-server routes. The setting was silently ignored there, so enabling it produced no authorization field on those request spans.

Upgrade Notes

  • Audit log consumers must read objects, not strings. If your pipeline JSON-decodes the audit fields a second time to get at their contents, that step now fails or double-decodes — read them directly instead. The previous string form parsed only by luck: Rust Debug escapes non-ASCII as \u{1F600}, which is not valid JSON, so any field carrying such text would have broken the parse outright. Consumers that already tolerate both shapes need no change, and rollout order does not matter for them.

v0.13.3 (2026-07-24)

Based on Lakekeeper OSS v0.13.3.

Highlights

  • Admission-gate roles now take effect. Roles granted by a role_granting admission check are finally evaluated by authorization — completing the external admission gate shipped in v0.13.0, whose granted roles previously never reached Cedar.

Features

  • Apply admission-gate roles in Cedar authorization. A new AdmissionRoleProvider (an uncached role-provider-chain leaf, mirroring the token role provider) serves the caller's admission-granted roles under their configured provider id; Cedar materialises them as Role entities and evaluates policies against them. Plus wires one provider per role-provider id the gate can mint under, so a gate-only deployment still resolves roles.
  • No-Access page for instance-level 403. An authenticated caller denied access to the instance (e.g. rejected by the admission gate) now lands on a dedicated No-Access page with proper 403 routing, instead of a broken view. (console v0.16.4)

Upgrade Notes

  • Conflicting role-provider ids now fail fast. A gate-minted role_provider_id that collides with a configured or token role-provider id is rejected at startup with ExtraProviderIdConflict — give the gate's minted roles a distinct provider id.
  • Admission roles apply under Cedar only. With the OpenFGA authorizer, or Cedar in externally-managed mode, admission-granted roles are not applied; the server logs a warning at startup so a misconfiguration is visible.

v0.13.2 (2026-07-23)

Based on Lakekeeper OSS v0.13.3.

Bug Fixes

  • Maintenance page. Console bump to 0.16.3 (console-components 0.17.2, console-plus-components 0.11.0). Redesigned maintenance date-range filters, suppressed spurious 403 notifications for maintenance tasks, and fixed the Home page chart title.

v0.13.1 (2026-07-20)

Based on Lakekeeper OSS v0.13.3.

Highlights

  • Okta role provider. Resolve a user's Okta group memberships to Lakekeeper roles via the Okta management API — private-key-JWT client auth with DPoP (RFC 9449) proofs enabled by default.
  • Provider-synced roles are now protected. Roles owned by a configured role provider (Okta, Entra, LDAP, or token IdP) can no longer be mutated through the management API, so the next provider sync can't silently clobber manual edits.

Features

  • Okta role provider (with DPoP). Group memberships resolved via GET /users/{id}/groups (Link-header pagination, keyed by immutable group id). OAuth2 client-credentials + private-key-JWT (JWK or PEM key); DPoP on by default with ephemeral P-256 proofs and nonce challenge/replay handling — opt out to Bearer. Requires the okta.users.read scope; wrapped in the shared role cache. See the Okta role-provider docs.
  • Managed-role write protection. The Cedar authorizer now reports its configured provider namespaces to the management-API guard, so create / update / delete / source-system rebind / member (un)assignment on a provider-owned role is rejected with 400 ManagedRoleImmutable. Native lakekeeper roles and the reserved system namespace are never included, so API-native and catalog-managed roles stay writable. Active only when a role provider is configured.
  • Post-logout redirect controls. Two new UI env vars — LAKEKEEPER__UI__OPENID_POST_LOGOUT_REDIRECT_URL and LAKEKEEPER__UI__OPENID_POST_LOGOUT_REDIRECT_DISABLED.
  • Static-asset caching in the UI server. Per-class Cache-Control plus weak ETag/304 on bundled assets: content-hashed assets/* are cached immutably and the DuckDB WASM is no longer re-downloaded on every load, while index.html stays uncached so runtime config placeholders remain fresh.

Bug Fixes

  • Maintenance page for warehouse-only permissions. Console bump to 0.16.1 (console-components 0.17.1) fixes the maintenance view for users who hold only warehouse-level permissions.

Upgrade Notes

  • Provider-role edits now return 400 ManagedRoleImmutable. If you previously edited provider-synced roles (Okta/Entra/LDAP/token) through the management API, those calls are now rejected — such edits were overwritten by the next sync anyway. Manage those roles at the source. No migration.
  • Building Plus from source: the Kubernetes client stack moved to k8s-openapi 0.28 / kube 4.0 (pulled in by the upstream limes 0.4.2 bump). Prebuilt binaries and images are unaffected.

Upstream Lakekeeper changes (up to Lakekeeper v0.13.3)

Notable for Plus users: - Configurable Kubernetes subject source. LAKEKEEPER__KUBERNETES_AUTHENTICATION_SUBJECT_SOURCE=username derives a service account's Lakekeeper user id from system:serviceaccount:<namespace>:<name> (stable across clusters) instead of the per-cluster uid (default, unchanged), so Kubernetes roles and instance admins can be pre-provisioned (lakekeeper#1899). - Reject role writes in provider-managed namespaces — the upstream API guard behind the managed-role protection above (lakekeeper#1891). - Stop evaluating a discarded Select on target-view load, avoiding a spurious authorization check (lakekeeper#1886).

v0.13.0 (2026-07-02)

Based on Lakekeeper OSS v0.13.1.

Highlights

  • Microsoft Entra ID (Graph) role provider. Resolve a user's transitive Entra group memberships into Lakekeeper roles — with secret, certificate, managed-identity, and workload-identity credentials, sovereign-cloud support, and built-in throttling/retry.
  • External admission gate. A new post-authentication seam can ask your control plane whether an already-authenticated caller may use this instance — for IdPs that issue broad, non-instance-scoped tokens — and contribute the caller's resolved roles.
  • Console overhaul. The bundled UI jumps to v0.13.2: a Files/storage explorer with in-browser Parquet/Avro/CSV preview, per-entity action menus, datasets as a first-class entity, redesigned view and table-health pages, a Role Members tab, and an enterprise usage-report builder.

Features

  • Entra ID / Microsoft Graph role provider. Paged transitiveMemberOf resolution; credential methods secret / certificate / managed-identity / workload-identity; public, US-gov, and China clouds; retries on 429 (honoring Retry-After) and transient 5xx.
  • AD range retrieval for LDAP attribute-mode groups. Active Directory returns >1500 group values under a ranged memberOf;range=… key; attribute mode now walks the range windows, so users in many groups are no longer silently truncated. OpenLDAP/389-DS behavior is unchanged.
  • External enforce-endpoint admission gate (lakekeeper-admission-enforce). Configurable named checks POST to your endpoint; the HTTP status is the decision (2xx allows and grants the check's role, 403 denies, anything else fails closed with 503 + Retry-After). Allow and deny are both cached; caller bearer-token relay is opt-in and never logged.
  • Persist OIDC token roles for DEFINER views. Opt-in via LAKEKEEPER__ROLE_PROVIDER_CHAIN__PERSIST_TOKEN_ROLES (default off) — mirrors a user's OIDC-token roles into the catalog so authorization can evaluate them when the user isn't the live caller, e.g. a DEFINER view running as its owner. Write-gated; no migration.
  • Generic-table parity in Cedar authorization. Non-Iceberg generic tables (e.g. Lance, Delta) now resolve and authorize through the Cedar surface exactly like tables and views.
  • Destructive-delete context for Cedar policies. force / purge / recursive and a warehouse soft_delete_enabled attribute are now in the Cedar request context, so a policy can forbid hard deletes that would bypass configured soft-deletion.
  • Role-membership actions in Cedar. The new manage/read role-assignment actions map to dedicated fine-grained Cedar actions, so policy authors control their bundling.
  • Destination-aware role source-system rebind. A dedicated update_source_system Cedar action exposes the target provider/source, so rebinds can be gated by destination — something the coarse upstream OpenFGA relation cannot express.
  • Per-decision policy trace in authorization audit. Audit events and the /check endpoint now record which Cedar policies determined each allow/deny outcome.
  • Schedule maintenance directly. expire_snapshots and remove_orphan_files can be triggered per table via the task-queue schedule endpoint, without waiting for a commit hook.

Bug Fixes

  • Corrupt-manifest orphan-files task no longer retries forever. A permanent failure (e.g. a corrupt Avro manifest) is now classified permanent and not requeued, instead of failing silently and re-running every day. Maintenance workers (remove_orphan_files, expire_snapshots) also persist a readable failure reason, surfaced in the task-details API — no server-log access required.

Breaking Changes

  • Default storage layout is now flat (inherited from upstream Lakekeeper 0.13): new namespaces use <base>/<tabular-uuid> instead of nesting tabulars under the parent-namespace UUID. Not retroactive — existing namespaces and paths are unchanged — so explicitly configure the full-hierarchy layout if you need the old behavior for new namespaces (lakekeeper#1853).

Upgrade Notes

  • Encrypted tables are skipped by maintenance. expire_snapshots and remove_orphan_files now detect Iceberg native encryption (format v3) via the immutable encryption.key-id property and skip such tables — Lakekeeper cannot read their encrypted manifests, and processing anyway risked deleting live data. Manually scheduling either task on an encrypted table returns 400.
  • Downgrade protection (upstream): serve refuses to start against a database already migrated by a newer binary. After a rollback, start the older binary with serve --force-start, accepting the schema-incompatibility risk (lakekeeper#1861).
  • Docker base images moved from Debian 12 (bookworm) to Debian 13 (trixie).
  • Building Plus from source: the catalog Postgres backend and the NATS/Kafka event backends are now separate upstream crates (lakekeeper-storage-postgres, lakekeeper-events-nats, lakekeeper-events-kafka). Prebuilt binaries and images are unaffected (lakekeeper#1812, lakekeeper#1814).

Upstream Lakekeeper changes (up to Lakekeeper v0.13.1)

Rolls up OSS v0.12.4, v0.13.0, and v0.13.1 (full list in the Lakekeeper release notes). Notable for Plus users: - Generic Table API — register non-Iceberg tables (Lance, Delta) as first-class generic tables with credential vending and full authorization (lakekeeper#1673, lakekeeper#1813); surfaced in Plus through the Cedar generic-table parity above. - Operator-owned warehouses. A managed_by marker locks warehouse spec mutations (delete, rename, (de)activate, storage profile, protection, format-version policy) to instance admins (lakekeeper#1828). - Authorizer-independent role-membership API — one management surface to list/add/remove a role's members regardless of the configured authorizer (lakekeeper#1829). - Multiple OIDC providers at once via LAKEKEEPER__OPENID_PROVIDERS (e.g. Okta for users + a cloud issuer for service accounts) (lakekeeper#1760). - Microsoft OneLake / Fabric storage profile, including workspace private-link endpoints (lakekeeper#1852). - Per-warehouse table format-version policy — allowed Iceberg format versions and an optional default per warehouse (lakekeeper#1786). - Customer-managed KMS encryption — warehouses with aws-kms-key-arn advertise s3.sse.type=kms, so vended-credential writes use your KMS key (lakekeeper#1847). - Cache hardening for large fleets — single-flight read-throughs and TTL jitter cut thundering-herd load on the database and on rate-limited STS/SAS endpoints (lakekeeper#1833, lakekeeper#1837). - /health now returns 503 (not 200) when unhealthy, so Kubernetes HTTP probes detect it (lakekeeper#1802). - Postgres migration locks are transaction-scoped, so a failed migration can't leak an advisory lock that blocks future migrations (lakekeeper#1790).

v0.12.2 (2026-05-26)

Highlights

  • Orphan-file cleanup now schedules itself adaptively per table — running more often where files accumulate and backing off where they don't — with a new dry-run mode.
  • The LDAP role provider can resolve groups via subtree Search and conditional Branching, not just the memberOf attribute.

Features

  • Adaptive orphan-file scheduling. The remove-orphan-files worker now self-tunes its cadence based on how fast reclaimable data builds up, and adds a dry-run mode that reports what it would delete. Orphan removal is now opt-in via enable-remove-orphan-files; default retention raised from 3 to 7 days. See the Table Maintenance docs for full config.
  • LDAP group resolution modes. Resolve group memberships via Search (paged subtree) or Branching (per-user-DN rules) in addition to the memberOf attribute; the resolution mode is recorded in audit logs.
  • Build metadata in Server Info. Server Info now reports Lakekeeper, Enterprise, and Console versions and commit SHAs, so deployed builds are easy to identify.

Breaking Changes

  • The orphan-files task-queue API was renamed remove_orphaned_files → remove_orphan_files (paths, config schemas, and worker/enable fields).

Upgrade Notes

  • Update any automation/IaC to the new remove_orphan_files task-queue path and schema names.
  • Orphan removal is now opt-in (it ran by default in 0.12.1): set enable-remove-orphan-files=true to keep it active. Default retention is now 7 days.

Upstream Lakekeeper changes (bump to v0.12.3)

  • Core and extension database migrations now apply atomically — no partial-migration state (lakekeeper#1768).
  • Fixed table property removal being lost when no properties remained (lakekeeper#1767).
  • New read-only maintenance mode for the server (lakekeeper#1765).
  • Users may now share an email address — the unique-email constraint was dropped (lakekeeper#1755).
  • New LAKEKEEPER__UI__ENABLE_SURVEYS flag to opt out of in-console surveys (lakekeeper#1750).

v0.12.1 (2026-05-10)

Features

  • Remove Orphan Files. New maintenance capability that reclaims storage by deleting data, manifest, and metadata files no longer referenced by any snapshot — available as a server background worker and as a remove-orphan-files subcommand (with dry-run). Enabled by default; set LAKEKEEPER__TASK_REMOVE_ORPHANED_FILES_WORKERS=0 to disable. Respects gc.enabled and per-table opt-out properties. See the Table Maintenance docs for full config.
  • Bounded orphan-files runtime. Cap how long a single orphan-files run may take with a configurable max run time.

Bug Fixes

  • Warehouse rename no longer leaves a stale name in the UI table preview (bundled UI 0.7.12).

Upgrade Notes

  • The orphan-files worker is enabled by default (2 workers); set LAKEKEEPER__TASK_REMOVE_ORPHANED_FILES_WORKERS=0 to disable. By default it only deletes files older than 3 days and honors gc.enabled / per-table opt-out.

Upstream Lakekeeper changes (bump to v0.12.2)

  • OpenFGA: rebuild/reconcile authorization tuples from the catalog, and support switching an existing server to OpenFGA (lakekeeper#1731, lakekeeper#1733).
  • OPA Trino batch authorization gains a broad-access fast path for warehouses/namespaces (lakekeeper#1727).
  • Storage: dropped the opendal dependency and now validates vended credentials via lakekeeper_io (lakekeeper#1737).
  • ADLS fixes: correct SAS-token key removal and %-encoding in blob names (lakekeeper#1746).

v0.12.0 (2026-04-21)

Highlights

  • Cedar authorization matured into a configurable, inspectable system: derive user attributes from identity fields, reference roles by global ID, and use a new resolve-entities API + Console tabs to see exactly what drives a decision.
  • Role providers resolve user roles from external sources — including LDAP groups and table properties — with caching, metrics, and audit.
  • Console: a visual Cedar Policy Builder (beta) with a Cedar-aware editor, authorization-inspection tabs, and new statistics dashboards.
  • Container images now default to ubi10 (breaking — see below).

Features

  • Cedar user identity derivations. Extract attributes from identity fields with named-capture regex rules (optional lowercase/uppercase transform) and match policies on the derived values.
  • Global role IDs in policies. Reference provider-scoped global role IDs as Cedar property values, and use short-form roles without a default provider.
  • Resolve-entities API. POST /management/v1/permissions/cedar/resolve-entities returns the Cedar entities for any resource — for debugging why a decision was reached.
  • SelectView action. Adds select / SelectView (and grant_select) for views, aligning with the upstream data-plane authorization split.
  • Role-provider subsystem. LDAP Group Provider + token-provider chain, roles parsed from table properties, caching with stale-fallback and metrics, and an opt-in audit event for resolved roles — wired into the Cedar authorizer. Configure via ROLE_PROVIDER_FILE (TOML), overridable per-field by env vars.
  • Richer permission-introspection audit. introspect_permissions logs now include the inner check tuples and their individual decisions.
  • Console: Cedar Policy Builder (beta). Visual editor/builder with a CodeMirror Cedar editor (highlighting, autocomplete, inline diagnostics, format/validate via cedar-wasm) and live Evaluate.
  • Console: authorization inspection + dashboards. Tabs for entity/policy sources, schema, and resolve-entities; new Home and Warehouse statistics dashboards; storage-layout configuration.

Bug Fixes

  • Cedar: correctness fixes around short-form role tags, per-request provider-ID derivation, Role subjects, and resolve-entities server gating.
  • Maintenance: expire-snapshots now removes statistics / partition-statistics from metadata, avoiding dangling references to deleted files.
  • TLS: added webpki and native root certs to the S3 client and UBI images, fixing handshake failures in some environments.
  • Console: correct handling of sub-namespaces containing dots; per-tab 403 keeps the navigation rail visible.

Breaking Changes

  • Container images now default to ubi10; the ubi9-based image remains available under a separate tag.

Upgrade Notes

  • If you pin the ubi9 base image (e.g. FIPS/compliance), switch to the dedicated ubi9 tag — the default is now ubi10.
  • Role-provider config can be supplied via ROLE_PROVIDER_FILE (TOML), with env vars overriding per field — review precedence if you set both.

Upstream Lakekeeper changes

  • Instance Admins — server-wide admin role independent of project membership (lakekeeper#1716).
  • Idempotency keys for safely retrying mutating requests (lakekeeper#1671).
  • referenced-by to discover views referencing a table/view (lakekeeper#1627).
  • Configurable trusted engines in request metadata for authorization (lakekeeper#1629).
  • Protect immutable table properties (e.g. encryption.key-id) during commits (lakekeeper#1700).
  • Faster list namespaces/tables/views (lakekeeper#1618).