Skip to content

Commit ce3ffd4

Browse files
Merge branch 'master' into fix/oidc-client-token-reuse
Only conflict was .secrets.baseline, which is generated: master had newer entries while this branch carried two shifted line numbers for test_oidc_auth_client.py and a newer generated_at. Resolved by taking master's baseline and re-running the detect-secrets hook, which reapplied exactly the two line-number shifts this branch needs (29 to 44, 31 to 46). Hand-merging generated line numbers would have been guesswork. 326 permissions tests pass on the merged tree, ruff and mypy clean. Signed-off-by: Larry Singleton <166439969+larrysingleton007@users.noreply.github.com>
2 parents 840eb04 + 97b0f25 commit ce3ffd4

43 files changed

Lines changed: 1827 additions & 131 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.secrets.baseline

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@
957957
"filename": "infra/feast-operator/api/v1/featurestore_types.go",
958958
"hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c",
959959
"is_verified": false,
960-
"line_number": 958
960+
"line_number": 969
961961
}
962962
],
963963
"infra/feast-operator/api/v1/zz_generated.deepcopy.go": [
@@ -980,7 +980,7 @@
980980
"filename": "infra/feast-operator/api/v1/zz_generated.deepcopy.go",
981981
"hashed_secret": "c2028031c154bbe86fd69bef740855c74b927dcf",
982982
"is_verified": false,
983-
"line_number": 1595
983+
"line_number": 1605
984984
}
985985
],
986986
"infra/feast-operator/api/v1alpha1/featurestore_types.go": [
@@ -1172,7 +1172,7 @@
11721172
"filename": "infra/feast-operator/internal/controller/services/repo_config.go",
11731173
"hashed_secret": "e2fb052132fd6a07a56af2013e0b62a1f510572c",
11741174
"is_verified": false,
1175-
"line_number": 235
1175+
"line_number": 241
11761176
}
11771177
],
11781178
"infra/feast-operator/internal/controller/services/services.go": [
@@ -1564,5 +1564,5 @@
15641564
}
15651565
]
15661566
},
1567-
"generated_at": "2026-07-31T17:08:59Z"
1567+
"generated_at": "2026-08-14T05:24:08Z"
15681568
}

SECURITY.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Security Policy
2+
3+
The Feast community takes security bugs seriously, and we appreciate the effort it takes to find and report them. We follow [GitHub's coordinated disclosure process](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/about-coordinated-disclosure-of-security-vulnerabilities) so that a fix can be prepared before details become public.
4+
5+
## Reporting a vulnerability
6+
7+
Report vulnerabilities privately through GitHub, using **[Report a vulnerability](https://github.com/feast-dev/feast/security/advisories/new)** on this repository's Security tab. Only the maintainers can see the report, and you will be credited on the published advisory if you would like to be.
8+
9+
Before reporting, please check the [published advisories](https://github.com/feast-dev/feast/security/advisories) to confirm the issue has not already been addressed.
10+
11+
A report needs to show a clear, reproducible security impact. Please include:
12+
13+
- the affected version or commit, and the configuration involved
14+
- a proof of concept, or steps that reproduce the issue
15+
- the actual impact, rather than a theoretical concern
16+
17+
Raw scanner or dependency-audit output does not meet that bar on its own, since it does not establish that the issue is reachable in Feast. Reports that have not been manually verified against Feast, including bulk, automated, or AI-generated submissions, may be closed without further response.
18+
19+
> [!WARNING]
20+
> Do not open a public GitHub issue, pull request, or Slack message for a security vulnerability. Those are visible to everyone and disclose the problem before a fix exists.
21+
22+
For anything that is not a vulnerability, including hardening suggestions and questions about how Feast's authentication and authorization work, a normal [GitHub issue](https://github.com/feast-dev/feast/issues) is the right place.
23+
24+
## Supported versions
25+
26+
Security fixes are applied to the latest release. Feast releases roughly monthly and offers best-effort community support, as described in the [versioning policy](docs/project/versioning-policy.md); there is no long-term support branch, so upgrading to the current release is the supported way to receive a fix.
27+
28+
## Published advisories
29+
30+
Past advisories for this project are listed under [Security advisories](https://github.com/feast-dev/feast/security/advisories).

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
* [Snowflake](reference/compute-engine/snowflake.md)
189189
* [AWS Lambda (alpha)](reference/compute-engine/lambda.md)
190190
* [Spark (contrib)](reference/compute-engine/spark.md)
191+
* [SparkApplication](reference/compute-engine/spark_application.md)
191192
* [Apache Flink](reference/compute-engine/flink.md)
192193
* [Ray (contrib)](reference/compute-engine/ray.md)
193194
* [Feature repository](reference/feature-repository/README.md)

docs/how-to-guides/feast-operator/05-security.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ spec:
9393

9494
### Advanced OIDC options
9595

96+
{% hint style="warning" %}
97+
Every option in this section requires `apiVersion: feast.dev/v1`. Under the deprecated
98+
`feast.dev/v1alpha1`, `authz.oidc` accepts only `secretRef`. The CRD has no conversion
99+
webhook, so a resource submitted as v1alpha1 is validated against the v1alpha1 schema and
100+
any other field is pruned without error rather than rejected. Applying the example below
101+
as v1alpha1 therefore leaves OIDC configured by Secret alone, with none of these settings
102+
taking effect and nothing in the output to say so. Use v1, which is the storage version.
103+
{% endhint %}
104+
96105
```yaml
97106
authz:
98107
oidc:
@@ -101,9 +110,18 @@ authz:
101110
secretKeyName: client_id # override the default Secret key name
102111
tokenEnvVar: FEAST_TOKEN # env var from which servers read the Bearer token
103112
verifySSL: false # disable SSL verification (dev only)
104-
caCertConfigMap: oidc-ca-cert # ConfigMap with CA cert for SSL verification
113+
caCertConfigMap: # ConfigMap with CA cert for SSL verification
114+
name: oidc-ca-cert
115+
jwksCacheLifespanSeconds: 300 # how long servers reuse the fetched JWK set
116+
jwksRequestTimeoutSeconds: 10 # network timeout for the JWKS fetch
105117
```
106118

119+
`jwksCacheLifespanSeconds` is not only a performance setting: it also bounds how long a key the provider has **revoked** continues to validate tokens. Lower it if your provider rotates or revokes aggressively, at the cost of proportionally more JWKS fetches. Key rotations that introduce a new key id are picked up immediately regardless, because an unknown key id forces a refetch. `jwksRequestTimeoutSeconds` bounds how long an unresponsive provider can block request serving. Both must be at least 1. When unset, neither key is written to the generated configuration and the feature server applies its own defaults (300 and 10 seconds respectively).
120+
121+
{% hint style="warning" %}
122+
These two options require a feature server image that recognizes them. The operator deploys a matching image by default, so this only applies if you pin an older one explicitly, through a container `image` override or the operator's `RELATED_IMAGE_FEATURE_SERVER` setting. An image that predates these options rejects its configuration at startup, so leave them unset until the pinned image is updated.
123+
{% endhint %}
124+
107125
**SDK docs**: [Feast OIDC Auth](../../getting-started/components/authz_manager.md#oidc-authorization)
108126

109127
---

docs/how-to-guides/feast-operator/06-batch-and-jobs.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,53 @@ spec:
5050
configMapKey: config # key inside the ConfigMap (default: "config")
5151
```
5252
53+
### SparkApplication batch engine (optional)
54+
55+
For Bring Your Own Spark on Kubernetes, use `spark_application` instead of in-process Spark.
56+
The Feast Operator auto-creates RBAC for this type. See
57+
[SparkApplication](../reference/compute-engine/spark_application.md) for the full config reference.
58+
Build an image from the reference
59+
[Dockerfile](https://github.com/feast-dev/feast/blob/master/sdk/python/feast/infra/compute_engines/spark_application/Dockerfile)
60+
(or equivalent):
61+
62+
```yaml
63+
apiVersion: v1
64+
kind: ConfigMap
65+
metadata:
66+
name: feast-spark-application-engine
67+
data:
68+
config: |
69+
type: spark_application
70+
image: my-registry.example.com/feast-spark-driver:latest
71+
namespace: feast
72+
executor_instances: 2
73+
driver_memory: "2g"
74+
executor_memory: "2g"
75+
```
76+
77+
```yaml
78+
apiVersion: feast.dev/v1
79+
kind: FeatureStore
80+
metadata:
81+
name: sample-spark-application
82+
spec:
83+
feastProject: my_project
84+
batchEngine:
85+
configMapRef:
86+
name: feast-spark-application-engine
87+
configMapKey: config
88+
# Optional: use the Spark driver image for feast-apply / init containers
89+
# services:
90+
# initImage: my-registry.example.com/feast-spark-driver:latest
91+
```
92+
5393
### Engine types
5494

5595
| `type` | Notes |
5696
|--------|-------|
5797
| `local` | Default; in-process Python, no extra infra |
5898
| `spark` | Apache Spark; requires a Spark operator or standalone cluster |
99+
| `spark_application` | Kubeflow Spark Operator `SparkApplication` CRs; requires Spark Operator + custom image; operator auto-creates RBAC |
59100
| `ray` | Ray cluster; requires a Ray operator |
60101
| `bytewax` | Bytewax streaming engine |
61102
| `snowflake.engine` | Snowflake Snowpark compute |

docs/project/contributing.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ After familiarizing yourself with the documentation, the simplest way to get sta
55
1. Setup your developer environment by following [development guide](development-guide.md).
66
2. Either create a [GitHub issue](https://github.com/feast-dev/feast/issues) or make a draft PR (following [development guide](development-guide.md)) to get the ball rolling!
77

8+
> **Reporting a security vulnerability?** Do not open an issue or PR. Report it privately through [GitHub's advisory form](https://github.com/feast-dev/feast/security/advisories/new); see the [security policy](https://github.com/feast-dev/feast/blob/master/SECURITY.md).
9+
810
## Decision making process
911
*See [governance](../../community/governance.md) for more details here*
1012

docs/reference/compute-engine/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ An example of built output from FeatureBuilder:
5757
- Supports point-in-time joins and large-scale materialization
5858
- Integrates with `SparkOfflineStore` and `SparkMaterializationJob`
5959

60+
### ☸️ SparkApplicationComputeEngine
61+
62+
{% page-ref page="spark_application.md" %}
63+
64+
- Batch materialization via Kubeflow Spark Operator `SparkApplication` CRs
65+
- One SparkApplication per materialize call (multi–feature-view batching)
66+
- Requires network-accessible online/offline/registry stores (no file-based backends)
67+
6068
### 🌊 FlinkComputeEngine
6169

6270
{% page-ref page="flink.md" %}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# SparkApplication Compute Engine
2+
3+
## Description
4+
5+
The **SparkApplication** compute engine runs Feast **batch materialization** on Kubernetes by creating a [Kubeflow Spark Operator](https://github.com/kubeflow/spark-operator) `SparkApplication` custom resource for each materialization job.
6+
7+
Unlike the in-process [`spark.engine`](spark.md) compute engine (which uses a Spark session inside the Feast process), `spark_application` submits work to the Spark Operator. The operator starts a driver pod and executors from your configured image; Feast polls the SparkApplication until it completes.
8+
9+
| Capability | Supported |
10+
|------------|-----------|
11+
| `materialize` / `materialize-incremental` | Yes |
12+
| Multiple feature views in one job | Yes — one SparkApplication per materialize call |
13+
| `get_historical_features` | Not yet |
14+
| SparkConnect | Separate approach — not this engine |
15+
16+
### Design
17+
18+
1. Feast creates a ConfigMap with job tasks and a driver copy of `feature_store.yaml`.
19+
2. Feast creates a `SparkApplication` CR pointing at the driver entrypoint (`main.py` in the image).
20+
3. Inside the pod, the batch engine type is rewritten to `spark.engine` so materialization uses the Spark session created by `spark-submit` (avoids recursive SparkApplication creation).
21+
4. The driver writes features to your configured **online store** and updates the **registry** (same network backends as the server).
22+
23+
### Requirements
24+
25+
- Kubeflow Spark Operator installed and watching the target namespace.
26+
- A container **image** that includes the Feast SDK, PySpark, and clients for your stores. See the reference [Dockerfile](https://github.com/feast-dev/feast/blob/master/sdk/python/feast/infra/compute_engines/spark_application/Dockerfile).
27+
- **Network-accessible** online store, offline store, and registry. File-based backends are rejected because Spark pods have an ephemeral filesystem:
28+
29+
| Rejected | Examples | Use instead |
30+
|----------|----------|-------------|
31+
| File online | `sqlite`, `faiss` | Redis, remote online, etc. |
32+
| File offline | `dask`, `file`, `duckdb` | `spark`, Postgres, Snowflake, BigQuery, etc. |
33+
| File registry | `file` | SQL registry, Snowflake |
34+
35+
For distributed reads, configure `offline_store.type: spark` (or another store Spark can read efficiently).
36+
37+
### Kubernetes / Feast Operator notes
38+
39+
When using the Feast Operator:
40+
41+
- Point `spec.batchEngine.configMapRef` at a ConfigMap whose `type` is `spark_application` (see [Guide 6 — Batch Engine & Scheduled Jobs](../../how-to-guides/feast-operator/06-batch-and-jobs.md)).
42+
- The operator auto-creates RBAC for the `spark_application` batch engine (server and driver service accounts).
43+
- Set `spec.services.initImage` if init / `feast-apply` containers need the Spark-capable image.
44+
45+
---
46+
47+
## Example
48+
49+
{% code title="feature_store.yaml" %}
50+
```yaml
51+
project: my_project
52+
registry:
53+
registry_type: sql
54+
path: postgresql+psycopg://feast:****@postgres:5432/feast
55+
online_store:
56+
type: redis
57+
connection_string: redis:6379
58+
offline_store:
59+
type: spark
60+
spark_conf:
61+
spark.master: local[*]
62+
batch_engine:
63+
type: spark_application
64+
image: my-registry.example.com/feast-spark-driver:latest
65+
namespace: feast
66+
spark_version: "4.0.1"
67+
driver_cores: 1
68+
driver_memory: "2g"
69+
executor_instances: 2
70+
executor_cores: 1
71+
executor_memory: "2g"
72+
spark_conf:
73+
spark.sql.shuffle.partitions: "100"
74+
```
75+
{% endcode %}
76+
77+
### Feast Operator ConfigMap
78+
79+
```yaml
80+
apiVersion: v1
81+
kind: ConfigMap
82+
metadata:
83+
name: feast-spark-batch-engine
84+
namespace: feast
85+
data:
86+
config: |
87+
type: spark_application
88+
image: my-registry.example.com/feast-spark-driver:latest
89+
namespace: feast
90+
executor_instances: 2
91+
driver_memory: "2g"
92+
executor_memory: "2g"
93+
---
94+
apiVersion: feast.dev/v1
95+
kind: FeatureStore
96+
metadata:
97+
name: feast
98+
namespace: feast
99+
spec:
100+
feastProject: my_project
101+
batchEngine:
102+
configMapRef:
103+
name: feast-spark-batch-engine
104+
configMapKey: config
105+
```
106+
107+
---
108+
109+
## Remote materialization
110+
111+
If the client uses a **remote** online store (`online_store.type: remote`), `FeatureStore.materialize()` delegates to the feature server HTTP API. The server runs the SparkApplication engine.
112+
113+
- Default (`run_async=False`): block until the server finishes sync materialization.
114+
- `run_async=True`: accept asynchronously (`?async=true`); poll feature-view state in the registry for completion.
115+
- `force=True` (with `run_async=True`): override stuck `MATERIALIZING` state on the server.
116+
117+
```python
118+
from datetime import datetime, timedelta
119+
from feast import FeatureStore
120+
121+
store = FeatureStore(repo_path=".") # client feature_store.yaml with online_store.type: remote
122+
123+
store.materialize(
124+
start_date=datetime.utcnow() - timedelta(days=1),
125+
end_date=datetime.utcnow(),
126+
)
127+
```
128+
129+
---
130+
131+
## Configuration reference
132+
133+
| Field | Type | Default | Description |
134+
|-------|------|---------|-------------|
135+
| `type` | string | `spark_application` | Engine type key |
136+
| `image` | string | **required** | Container image for the Spark driver/executors |
137+
| `image_pull_secrets` | list[str] | `[]` | Image pull secret names |
138+
| `namespace` | string | `default` | Namespace for SparkApplication and ConfigMap |
139+
| `service_account` | string | `""` | Driver service account; empty uses platform/operator default |
140+
| `spark_version` | string | `4.0.1` | Spark version for the CR |
141+
| `driver_cores` | int | `1` | Driver cores |
142+
| `driver_memory` | string | `1g` | Driver memory |
143+
| `executor_instances` | int | `1` | Number of executors |
144+
| `executor_cores` | int | `1` | Cores per executor |
145+
| `executor_memory` | string | `1g` | Memory per executor |
146+
| `spark_conf` | dict | `null` | Extra Spark configuration |
147+
| `hadoop_conf` | dict | `null` | Extra Hadoop configuration |
148+
| `env` | list[dict] | `[]` | Driver env vars (`name` + `value` or `valueFrom`) |
149+
| `env_from` | list[dict] | `[]` | EnvFrom sources |
150+
| `queue_name` | string | `null` | Optional queue / Kueue label |
151+
| `job_timeout_seconds` | int | `3600` | Max wait for SparkApplication completion |
152+
| `poll_interval_seconds` | int | `10` | Status poll interval |
153+
| `ttl_seconds_after_finished` | int | `3600` | CR TTL after finish |
154+
| `restart_policy` | string | `Never` | SparkApplication restart policy |
155+
| `max_retries` | int | `3` | Retries when restart policy allows |
156+
| `concurrency` | int | `1` | Parallel feature views inside one driver |
157+
| `labels` | dict | `{}` | Extra labels on the CR |
158+
| `volumes` / `volume_mounts` | list | `[]` | Extra volumes for the driver |
159+
| `py_files` | list[str] | `[]` | Additional Python files for Spark |
160+
| `node_selector` | dict | `null` | Pod node selector |
161+
| `tolerations` | list | `[]` | Pod tolerations |
162+
| `staging_location` | string | `null` | Reserved for historical retrieval (ignored for materialize) |
163+
164+
---
165+
166+
## Troubleshooting
167+
168+
| Symptom | What to check |
169+
|---------|----------------|
170+
| SparkApplication Pending / insufficient CPU | Lower resource requests via `spark_conf` (for example `spark.kubernetes.driver.request.cores`) or free cluster capacity |
171+
| ImagePullBackOff | Image name, tag, and `image_pull_secrets` |
172+
| 403 on ConfigMap or SparkApplication | RBAC for the Feast server and Spark driver service accounts |
173+
| Init `ValueError` about file-based stores | Switch online/offline/registry to network backends |
174+
| Init / feast-apply failures missing Spark deps | Use a Spark-capable image (`initImage` with the Feast Operator) |
175+
176+
---
177+
178+
## Related
179+
180+
- [Spark compute engine (in-process)](spark.md)
181+
- [Feast Operator — batch engine ConfigMap](../../how-to-guides/feast-operator/06-batch-and-jobs.md)
182+
- [Creating a custom compute engine](../../how-to-guides/customizing-feast/creating-a-custom-compute-engine.md)

docs/reference/feast-cli-commands.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Commands:
2828
materialize Run a (non-incremental) materialization job to...
2929
materialize-incremental Run an incremental materialization job to ingest...
3030
permissions Access permissions
31+
registry Manage the feature registry
3132
registry-dump Print contents of the metadata registry
3233
teardown Tear down deployed feature store infrastructure
3334
version Display Feast SDK version
@@ -483,6 +484,18 @@ reader driver_hourly_stats_fresh FeatureView DESCRIBE
483484
```
484485

485486

487+
## Registry
488+
489+
### create-schema
490+
491+
Pre-create the SQL registry schema so the application does not need DDL privileges at runtime. Use this with `schema_mode: verify` or `schema_mode: skip` in your `feature_store.yaml`.
492+
493+
```text
494+
feast registry create-schema
495+
```
496+
497+
This command only applies to SQL-based registries (`registry_type: sql`). It is safe to run multiple times — existing tables are not modified.
498+
486499
## Teardown
487500

488501
Tear down deployed feature store infrastructure

0 commit comments

Comments
 (0)