Skip to content

Fail closed Nova model HTTP without a policy and a witness - #41

Merged
cursor[bot] merged 1 commit into
mainfrom
cursor/nova-gate-default-on-fbb8
Oct 7, 2026
Merged

cursor[bot] merged 1 commit into
mainfrom
cursor/nova-gate-default-on-fbb8

Conversation

@warheart1984-ctrl

Copy link
Copy Markdown
Owner

What this is

Nova model HTTP no longer calls the provider when NOVA_ICK_POLICY and NOVA_ICK_SERVICE are both unset. Nothing is sent unless a policy and a witness are configured. This does not prove observed effect.

What a reviewer can check

  • With both env vars unset, POST /v1/chat and POST /v1/chat/completions return HTTP 403 WITNESS_REQUIRED, and the provider is not called. The same holds for post_json, the local-model tool, and the Ollama path in nova/api.py. A witness with no policy does not send, and the witness is not asked.
  • WICKET_ALLOW_DIRECT_CALLS sends only when it is exactly 1. Other values send nothing and print no warning. 1 sends and prints the stderr warning once per use. It does not bypass a policy that is already set, and it does not apply to gossip.
  • With a policy and an in-process witness, /v1/chat is sent by the witness. Nova's post_json is not called. The allow receipt's call_digest is the digest derived from the concrete POST (effect write). A streamed known-shape call is refused with STREAM_NOT_BOUND.

What this does not prove

  • Observed effect, observer mode, state_ref, retries, a heartbeat, and a two-account probe.
  • Egress. A caller who can reach the target without the witness is outside this control.
  • risk and action are still the caller's claim. The clock is unproven. A compromised witness can sign a false execution. The authorization header is presence-only. User-Agent is sent and is not hashed. A streamed known-shape call stays refused because the witness binds one body. The opt-out does not derive a call_digest.

Limits are stated in docs/executor-mode.md.

Open in Web Open in Cursor 

With NOVA_ICK_POLICY and NOVA_ICK_SERVICE unset, Nova does not call the provider client. WICKET_ALLOW_DIRECT_CALLS=1 remains the only local-dev escape and warns on every use. A configured gate still sends through the witness with the derived call_digest.

Co-authored-by: Jon Halstead <warheart1984@gmail.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T13:30:34.501748Z 173172e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 173172e373

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return self._from_provider_json(governed_request, data)

def chat_completion(self, governed_request: dict[str, Any]) -> dict[str, Any]:
refuse_unconfigured_model_http()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject streamed requests before starting the response

When stream=true and neither a policy nor the direct-call opt-out is configured, this new refusal is raised only while _stream_provider_completion is iterating, after StreamingResponse has already committed an HTTP 200 response. That generator catches KernelRefusal as ProviderError and emits an assistant-style [ProviderError] SSE chunk instead of the documented HTTP 403 WITNESS_REQUIRED, so streaming clients can interpret a denied request as a successful completion. Preflight the refusal before constructing StreamingResponse, or otherwise preserve the 403 semantics for this case.

Useful? React with 👍 / 👎.

@cursor
cursor Bot merged commit 7640528 into main Oct 7, 2026
5 checks passed
@warheart1984-ctrl
warheart1984-ctrl deleted the cursor/nova-gate-default-on-fbb8 branch October 15, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants