.agents/skills/kibana-privilege-deprecation/SKILL.md
Guides implementing and reviewing backward-compatible Kibana feature privilege deprecations using the deprecated privilege mapping framework.
alpha -> beta)deprecated property that is frozen for backward compatibility. Not shown in role management UI; privileges still registered in Elasticsearch.replacedBy mapping: Links each deprecated privilege to equivalent non-deprecated privilege(s). Required on every privilege of a deprecated feature.kibana_system cannot alter roles - this is a security constraint driving the entire design.Register new feature(s) with desired privileges using deps.features.registerKibanaFeature().
Add deprecated property with a user-facing notice string. Feature ID must stay unchanged.
deps.features.registerKibanaFeature({
deprecated: {
notice: i18n.translate('xpack.yourPlugin.featureDeprecationNotice', {
defaultMessage: 'Feature X is deprecated. Use Feature Y instead. See {link}.',
values: { link: 'https://...' },
}),
// Optional: override which features conceptually replace this one (for Spaces UI).
// By default derived from privilege-level replacedBy. Only needed when replacedBy
// references multiple features but you want the Spaces UI to show a subset.
replacedBy: ['feature_y'],
},
id: 'feature_x', // Must stay the same
name: 'Feature X (DEPRECATED)',
privileges: { /* keep original privileges unchanged, add replacedBy */ },
});
replacedBy on every privilegeEvery all, read, and sub-feature privilege must have replacedBy.
Simple form -- use when the deprecated feature has NO sub-features:
privileges: {
all: {
...originalAllPrivilege,
replacedBy: [
{ feature: 'feature_y', privileges: ['all'] },
],
},
read: {
...originalReadPrivilege,
replacedBy: [
{ feature: 'feature_y', privileges: ['read'] },
],
},
}
Extended { default, minimal } form -- use when the deprecated feature HAS sub-features:
When a deprecated feature has sub-features, the top-level all privilege implicitly includes all sub-feature privileges granted via includeIn: 'all', while minimal_all does not. These two paths must map differently to preserve the distinction. The same applies to read / minimal_read.
privileges: {
all: {
...originalAllPrivilege,
replacedBy: {
// `default` maps `all` (= minimal_all + auto-granted sub-feature privileges)
default: [
{ feature: 'feature_y', privileges: ['all', 'sub_feature_priv_id'] },
],
// `minimal` maps `minimal_all` (= top-level only, no sub-features)
minimal: [
{ feature: 'feature_y', privileges: ['minimal_all'] },
],
},
},
read: {
...originalReadPrivilege,
replacedBy: {
default: [
{ feature: 'feature_y', privileges: ['read', 'sub_feature_priv_id'] },
],
minimal: [
{ feature: 'feature_y', privileges: ['minimal_read'] },
],
},
},
}
// Each sub-feature privilege also needs its own replacedBy (simple array form):
// replacedBy: [{ feature: 'feature_y', privileges: ['sub_feature_priv_id'] }]
Rule of thumb: If the deprecated feature defines subFeatures, always use the { default, minimal } form on its top-level privileges. The simple array form is only correct when there are no sub-features (it applies the same mapping to both default and minimal).
security.authz.requiredPrivileges for authorization. Ensure deprecated feature's api array is updated so that both deprecated and replacement privilege holders can access the same endpoints.capabilities.new_feature.capability instead of capabilities.old_feature.capability. The framework auto-maps deprecated capabilities to replacement ones.Kibana will refuse to start if any of these are violated:
replacedBy on every privilegereplacedByfeature.deprecated.replacedBy feature IDs (if set) must be a subset of features used in privilege-level replacedByWhen reviewing deprecation PRs, focus on what startup validation does NOT catch:
deprecated.notice is localized (i18n.translate) with a link to docs or PRreplacedBy uses { default, minimal } form, not simple arrayapi entries, ui capabilities, app, catalogue, and management from the deprecated onessecurity.authz.requiredPrivileges; deprecated feature's api array matches replacement'scapabilities.new_feature.ui_all)For concrete code covering all deprecation scenarios (rename, split, sub-feature extraction, consolidation, alerting/cases), read the test plugin:
x-pack/platform/test/security_api_integration/plugins/features_provider/server/index.ts
For real-world deprecations (discover, dashboard, visualize, maps):
x-pack/platform/plugins/shared/features/server/oss_features.ts
| File | Purpose |
|---|---|
x-pack/platform/test/security_api_integration/tests/features/deprecated_features.ts | Integration tests for deprecated features |
x-pack/platform/plugins/shared/features/server/feature_registry.test.ts | Unit tests for validation |
x-pack/platform/plugins/shared/security/server/authorization/roles/elasticsearch_role.test.ts | Role deserialization tests |
dev_docs/key_concepts/api_authorization.mdx