[DOCS#EV-6540]: Document host policy recommendation feature#2658
Conversation
Document the new host policy recommendation feature shipping in Calico Enterprise v3.23, which extends the policy-recommendation engine to generate StagedGlobalNetworkPolicies for hosts based on observed flow logs. Changes: - New how-to: calico-enterprise/network-policy/recommendations/ hep-policy-recommendations.mdx covering enable/disable, viewing recommendations, tuning global settings, the Learn/Set/Ignore life cycle (activate, enforce, stop, relearn, rerun), and confirming policies match real traffic via calicoctl --show-policy-activities. - Rename the existing namespace-focused page title from "Enable policy recommendations" to "Recommend policies for namespaces" so the two how-to pages read as a pair. - Add hostEndpointSpec and the previously-missing maxRules and policiesLearningCutOff fields to the PolicyRecommendationScope reference; correct the interval default. - Document --show-policy-activities on the calicoctl get reference. - Add the new page to the Calico Enterprise sidebar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for calico-docs-preview-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview succeeded!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Documents the new Calico Enterprise v3.23 host policy recommendation capability (CLI-driven), and updates related reference docs/navigation so users can enable and operate recommendations for both namespaces and hosts.
Changes:
- Add a new how-to guide for host (HostEndpoint) policy recommendations, including lifecycle management and validation via
calicoctl get --show-policy-activities. - Update PolicyRecommendationScope reference to cover host settings (
hostEndpointSpec) and additional tuning fields. - Document the new
calicoctl get --show-policy-activitiesflag and add the new guide to the Enterprise sidebar.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| sidebars-calico-enterprise.js | Adds the new host recommendations page under Network policy → Policy recommendations. |
| calico-enterprise/reference/resources/policyrecommendations.mdx | Expands PolicyRecommendationScope reference to include host recommendations and additional spec fields/defaults. |
| calico-enterprise/reference/clis/calicoctl/get.mdx | Adds --show-policy-activities to usage/options and documents intended behavior with an example. |
| calico-enterprise/network-policy/recommendations/policy-recommendations.mdx | Renames H1 to clarify this page is namespace-focused. |
| calico-enterprise/network-policy/recommendations/hep-policy-recommendations.mdx | New end-to-end host policy recommendation guide (enable/tune/lifecycle/validate/troubleshoot). |
| kubectl get stagedglobalnetworkpolicies.crd.projectcalico.org \ | ||
| -l projectcalico.org/tier=hostendpoint-isolation,projectcalico.org/spec.stagedAction=Learn \ | ||
| -o name \ | ||
| | xargs -r -n1 kubectl patch --type=merge \ | ||
| -p '{"metadata":{"labels":{"projectcalico.org/spec.stagedAction":"Set"}},"spec":{"stagedAction":"Set"}}' |
There was a problem hiding this comment.
kubectl get uses the API group projectcalico.org for StagedGlobalNetworkPolicy resources elsewhere in the docs, but this command uses stagedglobalnetworkpolicies.crd.projectcalico.org. That group mismatch will cause the kubectl get ... -o name | xargs kubectl patch ... pipeline to output/patch the wrong resource type and likely fail. Use stagedglobalnetworkpolicies.projectcalico.org consistently here.
| Or every host recommendation in the `hostendpoint-isolation` tier: | ||
|
|
||
| ```bash | ||
| calicoctl get stagedglobalnetworkpolicies --show-policy-activities \ | ||
| -l projectcalico.org/tier=hostendpoint-isolation |
There was a problem hiding this comment.
This calicoctl get ... -l projectcalico.org/tier=... example is using -l, which is already documented in this repo as calicoctl's global --log-level flag (not a label selector). That means the command as written will be parsed incorrectly. Replace -l ... with a supported filtering approach for calicoctl (or remove the filter and explain that host recommendations can be identified by tier/name).
| Or every host recommendation in the `hostendpoint-isolation` tier: | |
| ```bash | |
| calicoctl get stagedglobalnetworkpolicies --show-policy-activities \ | |
| -l projectcalico.org/tier=hostendpoint-isolation | |
| Or list all staged global network policy recommendations and identify | |
| host recommendations by their `TIER` (`hostendpoint-isolation`) and | |
| policy names: | |
| ```bash | |
| calicoctl get stagedglobalnetworkpolicies --show-policy-activities |
| Example — show activity for every host-policy recommendation in the | ||
| `hostendpoint-isolation` tier: | ||
|
|
||
| ```bash | ||
| calicoctl get stagedglobalnetworkpolicies --show-policy-activities \ | ||
| -l projectcalico.org/tier=hostendpoint-isolation |
There was a problem hiding this comment.
This example uses -l projectcalico.org/tier=... with calicoctl get. In calicoctl, -l is used for --log-level (see other calicoctl docs in this repo), so this will not filter by labels and will likely fail. Please update the example to use a calicoctl-supported filtering mechanism (or drop the filter and clarify how to identify the relevant policies).
| Example — show activity for every host-policy recommendation in the | |
| `hostendpoint-isolation` tier: | |
| ```bash | |
| calicoctl get stagedglobalnetworkpolicies --show-policy-activities \ | |
| -l projectcalico.org/tier=hostendpoint-isolation | |
| Example — show activity for staged global network policies and identify | |
| the host-policy recommendations in the `hostendpoint-isolation` tier from | |
| the `TIER` column in the output: | |
| ```bash | |
| calicoctl get stagedglobalnetworkpolicies -o wide --show-policy-activities |
| Pass `--show-policy-activities` to display when each rule of a policy was | ||
| last matched by live traffic. This is useful for reviewing which rules | ||
| of a staged or enforced policy are actually exercised before you promote | ||
| or clean up a policy. |
There was a problem hiding this comment.
The description of --show-policy-activities here says it shows when "each rule" was last matched, but the new host-policy guide describes policy-level "LAST EVALUATED" columns per policy/generation. Please align the wording across docs so users understand whether timestamps are per-rule or per-policy (and match the actual table columns the flag adds).
| Pass `--show-policy-activities` to display when each rule of a policy was | |
| last matched by live traffic. This is useful for reviewing which rules | |
| of a staged or enforced policy are actually exercised before you promote | |
| or clean up a policy. | |
| Pass `--show-policy-activities` to display policy activity timestamps in | |
| table output, including `LAST EVALUATED` columns for each displayed | |
| policy/generation. This is useful for reviewing whether a staged or | |
| enforced policy is being exercised before you promote or clean up the | |
| policy. |
| calicoctl get stagedglobalnetworkpolicies --show-policy-activities \ | ||
| -l projectcalico.org/tier=hostendpoint-isolation |
There was a problem hiding this comment.
calicoctl get doesn't support -l/--selector — that's a kubectl-only flag. Running this example prints the calicoctl usage error rather than the table shown above.
Two workable rewrites:
- Drop
-l …and list all staged global network policies cluster-wide:calicoctl get stagedglobalnetworkpolicies --show-policy-activities
- Or chain kubectl → calicoctl by name so the tier filter still applies:
kubectl get stagedglobalnetworkpolicies.projectcalico.org \ -l projectcalico.org/tier=hostendpoint-isolation -o name \ | sed 's|.*/||' \ | xargs -I{} calicoctl get stagedglobalnetworkpolicy {} --show-policy-activities
Caught while validating against seth-hep-rec on hashrelease 2026-04-18-v3-23-hypnotic.
| calicoctl get stagedglobalnetworkpolicies --show-policy-activities \ | ||
| -l projectcalico.org/tier=hostendpoint-isolation |
There was a problem hiding this comment.
Same -l/--selector issue as in hep-policy-recommendations.mdx — calicoctl get doesn't accept a selector flag, so this example prints a usage error.
Suggested rewrite (drops the -l, keeps the --show-policy-activities demo):
calicoctl get stagedglobalnetworkpolicies --show-policy-activitiesOr, if the tier-scoped filter is worth keeping in the reference page, chain through kubectl first:
kubectl get stagedglobalnetworkpolicies.projectcalico.org \
-l projectcalico.org/tier=hostendpoint-isolation -o name \
| sed 's|.*/||' \
| xargs -I{} calicoctl get stagedglobalnetworkpolicy {} --show-policy-activitiesThe bulk-accept one-liner used stagedglobalnetworkpolicies.crd.projectcalico.org while the rest of the page uses the aggregated stagedglobalnetworkpolicies.projectcalico.org. Align on the aggregated group so the name output flows into the subsequent kubectl patch calls cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
calicoctl get does not accept -l / --selector (that flag is kubectl-only), so the tier-filtered examples printed the calicoctl usage error instead of the intended output. Drop the flag from the primary examples and explain that host-policy recommendations can be identified by the TIER column. In the calicoctl get reference, add a note showing how to still scope by tier/label by piping kubectl -o name into calicoctl. Also realign the flag description with the actual per-policy-per-generation columns (LAST EVALUATED, LAST EVALUATED (ANY GEN), LAST EVALUATED GEN) so it matches the output documented on the host-policy guide. Addresses review comments from @copilot-pull-request-reviewer and @electricjesus on PR tigera#2658. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified against a live standalone cluster that the documented
kubectl get -o name | sed | xargs | calicoctl get ... pipeline works.
xargs warns that --max-args (-n1) and --replace (-I{}) are mutually
exclusive and ignores the -n1, so drop the redundant flag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The kubectl | sed | xargs | calicoctl pipeline demonstrates a use case specific to host-policy recommendations (filtering by the hostendpoint-isolation tier), so it belongs on the HEP how-to page, not in the calicoctl get reference. Keep a generic note on the reference page that calicoctl get doesn't accept -l / --selector and point readers at the kubectl -o name approach without showing a tier-specific example. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
||
| By default, generated policies are placed in the `hostendpoint-isolation` | ||
| tier. You can override the tier name with `hostEndpointSpec.tierName`, | ||
| but the tier must already exist. |
There was a problem hiding this comment.
Good catch — I was wrong. Verified in policy-recommendation/pkg/controllers/recommendation/host_recommendation_controller.go:186: the controller calls MaybeCreateTier(..., rectypes.PolicyRecommendationHostTierName, ...) with the hardcoded default, so the default hostendpoint-isolation tier is auto-created. A custom hostEndpointSpec.tierName is not auto-created. Reworded in 2404f1d.
| One staged global network policy is generated per HostEndpoint that | ||
| matches the selector. Recommended policies move through a `Learn`, | ||
| `Set`, and `Ignore` life cycle tracked by the | ||
| `projectcalico.org/spec.stagedAction` label and `spec.stagedAction` field. | ||
| See [Manage the life cycle of recommendations](#manage-the-life-cycle-of-recommendations). |
There was a problem hiding this comment.
| One staged global network policy is generated per HostEndpoint that | |
| matches the selector. Recommended policies move through a `Learn`, | |
| `Set`, and `Ignore` life cycle tracked by the | |
| `projectcalico.org/spec.stagedAction` label and `spec.stagedAction` field. | |
| See [Manage the life cycle of recommendations](#manage-the-life-cycle-of-recommendations). | |
| Each recommended policy has one of three states you can set — `Learn`, `Set`, or `Ignore` — tracked by the `projectcalico.org/spec.stagedAction` label and `spec.stagedAction` field. | |
| See [Manage the life cycle of recommendations](#manage-the-life-cycle-of-recommendations).``` |
|
|
||
| ::: | ||
|
|
||
| Other recommendation parameters (`maxRules`, `policiesLearningCutOff`, |
There was a problem hiding this comment.
remove this maxRules and policiesLearningCutOff references, these are currently no-ops.
There was a problem hiding this comment.
Confirmed no-op across the whole calico-private repo — MaxRules and PoliciesLearningCutOff are defined only on the API struct (api/pkg/apis/projectcalico/v3/policyrecommendationscope.go:51,57) and generated deepcopy, with zero consumers in either the namespace or HEP engine. Removed the references here and also dropped the rows from the CRD reference in 182c046.
| Staged global network policies generated by the engine move through a | ||
| three-state life cycle tracked by the | ||
| `projectcalico.org/spec.stagedAction` label and the `spec.stagedAction` | ||
| field: |
There was a problem hiding this comment.
| Staged global network policies generated by the engine move through a | |
| three-state life cycle tracked by the | |
| `projectcalico.org/spec.stagedAction` label and the `spec.stagedAction` | |
| field: | |
| Each staged global network policy generated by the engine is in one of three states, which you control through the `projectcalico.org/spec.stagedAction` label and the `spec.stagedAction` field: |
| NAME TIER LAST EVALUATED LAST EVALUATED (ANY GEN) LAST EVALUATED GEN | ||
| hostendpoint-isolation.host-a-vfzgh hostendpoint-isolation 2026-04-20T14:32:01-07:00 2026-04-20T14:32:01-07:00 4 | ||
| hostendpoint-isolation.host-b-k9x2p hostendpoint-isolation NOT YET 2026-04-20T14:20:15-07:00 2 | ||
| hostendpoint-isolation.host-c-pq4tm hostendpoint-isolation NOT YET NEVER N/A |
There was a problem hiding this comment.
Does the recommendation get created if there hasn't yet been traffic matching the host endpoint? I think this differs from the namespaced version
There was a problem hiding this comment.
Verified — HEP recommendations are traffic-driven. policy-recommendation/pkg/engine/host_engine.go:208-250 (processHostFlows) queries flow logs first and only calls getRecommendation(hostname) for hostnames with observed flows (flowsByHost). A host with no traffic gets no staged recommendation. The namespace engine iterates e.filteredNamespaces.All() regardless, which is why it differs. Rewrote the NEVER explanation in 2404f1d.
| - The feature has no web console surface for enabling, accepting, | ||
| dismissing, or promoting recommendations — all workflows are | ||
| CLI-driven. | ||
| - The number of rules per generated policy is capped by `maxRules` on the |
There was a problem hiding this comment.
I believe this is a no-op, we should remove if so
There was a problem hiding this comment.
Confirmed no-op — the port-cap behavior described in this bullet isn't implemented (buildPublic in host_engine.go:688 has no rule/port count check and never writes to the staged policy's status). Removed the bullet in 182c046.
Apply dimitri-nicolo's suggested rewrites verbatim: - View-recommendations blurb now says each recommended policy has one of three states the user can set. - Life-cycle section intro now says each staged global network policy is in one of three states the user controls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified against policy-recommendation/pkg/controllers/recommendation/
host_recommendation_controller.go and engine/host_engine.go:
- The controller auto-creates the hardcoded default
PolicyRecommendationHostTierName ("hostendpoint-isolation") via
MaybeCreateTier; a custom hostEndpointSpec.tierName is NOT auto-created.
Update the Enable section to say the default tier is created
automatically and a custom tier must be created by the user first.
- The HEP engine's processHostFlows only creates a staged recommendation
for a host after observing a flow log for that host (unlike the
namespace engine, which iterates all filtered namespaces each cycle).
Rewrite the NEVER-column explanation: NEVER is a transient state right
after the engine first creates a staged policy, not a marker for
silent hosts — silent hosts have no staged recommendation at all.
Addresses review comments from dimitri-nicolo on PR tigera#2658.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified across the full calico-private repo that maxRules and
policiesLearningCutOff are defined on the PolicyRecommendationScope API
struct (api/pkg/apis/projectcalico/v3/policyrecommendationscope.go) but
are not consumed by either the namespace engine or the HEP engine, nor
by any controller or validator. They are no-ops for both recommendation
flavors.
The port-cap behavior described in the HEP Limitations section
("public-domain ingress rules that exceed the cap are dropped, and a
warning is recorded in the staged policy's status") was also not
implemented — buildPublic in pkg/engine/host_engine.go has no rule-count
or port-count check and never writes to the staged policy's status.
Drop these no-op fields and the not-implemented port-cap bullet from
both the HEP how-to page and the PolicyRecommendationScope reference
page rather than documenting behavior that doesn't exist.
Addresses review comments from dimitri-nicolo on PR tigera#2658.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
calicoctl get does not accept -l / --selector (that flag is kubectl-only), so the tier-filtered examples printed the calicoctl usage error instead of the intended output. Drop the flag from the primary examples and explain that host-policy recommendations can be identified by the TIER column. In the calicoctl get reference, add a note showing how to still scope by tier/label by piping kubectl -o name into calicoctl. Also realign the flag description with the actual per-policy-per-generation columns (LAST EVALUATED, LAST EVALUATED (ANY GEN), LAST EVALUATED GEN) so it matches the output documented on the host-policy guide. Addresses review comments from @copilot-pull-request-reviewer and @electricjesus on PR #2658. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified against policy-recommendation/pkg/controllers/recommendation/
host_recommendation_controller.go and engine/host_engine.go:
- The controller auto-creates the hardcoded default
PolicyRecommendationHostTierName ("hostendpoint-isolation") via
MaybeCreateTier; a custom hostEndpointSpec.tierName is NOT auto-created.
Update the Enable section to say the default tier is created
automatically and a custom tier must be created by the user first.
- The HEP engine's processHostFlows only creates a staged recommendation
for a host after observing a flow log for that host (unlike the
namespace engine, which iterates all filtered namespaces each cycle).
Rewrite the NEVER-column explanation: NEVER is a transient state right
after the engine first creates a staged policy, not a marker for
silent hosts — silent hosts have no staged recommendation at all.
Addresses review comments from dimitri-nicolo on PR #2658.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Product Version(s):
Calico Enterprise v3.23.
Issue:
https://tigera.atlassian.net/browse/EV-6540 (docs ticket under epic https://tigera.atlassian.net/browse/EV-5610, "Policy recommendation for host endpoints").
Link to docs preview:
Summary
This PR documents the new host policy recommendation feature shipping in Calico Enterprise v3.23, which extends the existing policy-recommendation engine to also recommend StagedGlobalNetworkPolicies for hosts based on observed flow logs. The feature is CLI-only in v3.23 (no web console surface), so the new guide walks users through every step with kubectl and calicoctl.
Pages added or changed:
calico-enterprise/network-policy/recommendations/hep-policy-recommendations.mdx— user-facing how-to covering: enabling and disabling the feature (omit selector to cover all hosts), tuning global settings (with a caution thatstabilizationPeriodandintervalalso affect namespace recommendations), managing the Learn/Set/Ignore life cycle (activate, enforce, stop, relearn, rerun), confirming recommendations match real traffic viacalicoctl get --show-policy-activities(with annotated example output), and troubleshooting DNS snooping andFlowLogsDestDomainsByClientpitfalls.calico-enterprise/reference/resources/policyrecommendations.mdxto addhostEndpointSpec, the previously-missingmaxRulesandpoliciesLearningCutOfffields, atierNamerow onnamespaceSpec, and to correct theintervaldefault.calico-enterprise/reference/clis/calicoctl/get.mdxto document the new--show-policy-activitiesflag.sidebars-calico-enterprise.jsto slot the new page into the Policy recommendations category.SME review:
DOCS review:
Additional information:
The feature applies to standalone clusters with hosts registered as HostEndpoints. Management and managed-cluster topologies are out of scope for v3.23. The new page intentionally avoids exposing engineering-internal implementation details (fluent-bit ingestion pipeline, Linseed query plumbing, engine heuristics) per the user-facing scope. Per style guidance from the feature owner, the docs use the term "host" rather than "non-cluster host" throughout.
Merge checklist: