Skip to content

feat(credentials): saved credentials for Amplitude, Kalshi, Splunk, CB Insights, Railway, and Qdrant; fix Kalshi Ed25519 signing - #9927

Merged
waleedlatif1 merged 16 commits into
stagingfrom
feat/saved-credentials-long-tail
Nov 12, 2026
Merged

waleedlatif1 merged 16 commits into
stagingfrom
feat/saved-credentials-long-tail

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Moves six integrations that took secrets inline onto saved credentials. Each one adds a token credential provider, a check validator that makes one cheap, read-only, documented, non-billable request, a credential policy on every tool, and a credential-only successor block built with withSavedCredential.
    • Amplitude (amplitude_v2): API key, secret key and region, checked with GET /api/2/events/list on the region's host. User Profile now sends both saved keys over Basic auth, the only scheme in Amplitude's User Profile docs. Inline calls that pass only the secret key keep the Api-Key header they sent before.
    • Kalshi (kalshi_v3): key ID and private key, checked with a signed GET /portfolio/balance. Only the operations that sign requests use the credential.
    • Splunk (splunk_v2): token and management URL, checked with authentication/current-context through guarded egress. TLS verification is never relaxed.
    • CB Insights (cbinsights_v2): client ID and secret, checked with POST /v2/authorize.
    • Railway (railway_v2): checks depend on the token type. An account token reads me, a workspace token reads workspace(workspaceId), and a project token reads projectToken with the Project-Access-Token header.
    • Qdrant (qdrant_v2): API key and instance URL, checked with GET /collections through guarded egress.
  • Fixes Kalshi request signing for Ed25519 keys, which is Kalshi's default key type. Before this, every authenticated call with an Ed25519 key failed.
  • Small additive extensions to shared code:
    • Token credential fields gain optional multiline and optional flags. optional applies to a domain or workspace field in pasted-token mode. The create contract, the connect form and the secret builder all honor it, and the token is always required.
    • tokenCredentialOAuth gains an optional tokenReplacesParams list.
    • The saved-credential contract accepts any credential param filled by the token, not only accessToken.
  • Not shipped:
    • RevenueCat: no documented v1 read proves the key without side effects. GET /subscribers/{id} creates a customer.
    • SAP S/4HANA: no read endpoint is guaranteed to be enabled for every Communication Arrangement.

Product decision: the legacy Splunk block is hidden

  • The legacy Splunk block is hidden from the toolbar, like every other block with a saved-credential successor. Existing Splunk blocks keep their username/password inputs and run unchanged.
  • A new Splunk block now needs token authentication. Splunk's docs say token authentication is off by default. Turning it on needs TLS, the KV Store and the edit_tokens_settings capability. Deployments that cannot turn it on can't add a new Splunk block until a username/password credential exists.
  • Follow-up design: a saved Splunk login can't be expressed today. A tool's credentialParams are fixed, so one splunk service can't fill authToken for one credential and username/password for another. Two options:
    • Add a login authMethod that exchanges the username and password at execution time (POST /services/auth/login) for a session key, sent as Authorization: Splunk <sessionKey>.
    • Let a credential policy choose its params per authMethod.
      Either one changes shared resolve code and the Splunk tools' auth scheme, so neither is in this PR. If reviewers prefer, the legacy block can stay in the toolbar until then.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • Each validator's failure modes were tested first and watched fail:
    • right host, auth scheme and egress
    • key refused
    • a 200 with an unexpected body not taken as proof
    • bad input refused before any request
  • Optional credential fields: required-field derivation, the stored blob, and a revert proof.
  • Amplitude User Profile goes through the executor with a saved credential (Basic auth over both keys; red before the fix). The inline secret-key-only call keeps the Api-Key header.
  • Kalshi signatures are verified for Ed25519 and PKCS#8 RSA keys, and unsupported key types are refused.
  • The successor-block serializer matrix covers all six integrations.
  • These checks pass:
    • check-block-registry (saved-credential contract)
    • block-successors
    • tool-metadata
    • deployment-config
    • integration-catalog
    • tool-param-reachability
  • Backward compatibility, for reviewers:
    • Legacy blocks keep their inline inputs; they are hidden from the toolbar and point to their successors.
    • Existing provider ids and stored secret shapes are unchanged.
    • The optional flag is set only by the new Railway descriptor.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Nov 12, 2026 2:58pm UTC

Request Review

@greptile-apps

greptile-apps Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Critical impact] The PR appears safe to merge; no new actionable defects were established.

Summary

Adds saved credentials for Amplitude, Kalshi, Splunk, CB Insights, Railway, and Qdrant.

  • Amplitude blocks can use a saved project credential.
  • CB Insights blocks can use a saved API client.
  • Kalshi blocks can use saved keys that sign Ed25519 and RSA requests.
  • New Splunk blocks use a saved authentication token.
  • Railway blocks can use account, workspace, and project tokens saved once.
  • Qdrant blocks can use a saved key and instance URL.
  • Saved credentials support multi-line keys and fields that are only sometimes required.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Enter credentials] --> B[Read-only provider check]
  B --> C[Store encrypted credential]
  C --> D[Select credential in successor block]
  D --> E[Authorize and resolve credential]
  E --> F[Fill tool parameters]
  F --> G[Call provider]
Loading

Reviews (5) · Last reviewed commit: "fix(amplitude): default the saved creden..." · Reviewed by Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 123 files

Confidence score: 3/5

  • The private key remains readable after the textarea loses focus in token-service-account-modal.tsx, so it can stay exposed while the modal is open. Mask it on blur and add an explicit reveal action.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/token-service-account-modal.tsx">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/token-service-account-modal.tsx:266">
P2: This private-key textarea stays fully visible after it loses focus, unlike `SecretInput`, leaving a trading key exposed while the modal remains open. Mask it on blur and provide an explicit reveal action.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1
waleedlatif1 force-pushed the feat/saved-credentials-long-tail branch from 77b2d26 to 077c9f8 Compare November 12, 2026 12:37
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 123 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
waleedlatif1 force-pushed the feat/saved-credentials-long-tail branch from 077c9f8 to 188816e Compare November 12, 2026 13:25
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 123 files

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 123 files

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

…es by default

Kalshi picks the verification algorithm from the registered public key: Ed25519 over the
pre-sign text, or RSA-PSS SHA-256 for an RSA key. The signer always passed the sha256 digest
and PSS padding, which Node and Bun refuse for an Ed25519 key, so every authenticated call
with a default key failed before it was sent. The key type is now read from the parsed key
(a PKCS#8 RSA key shares the Ed25519 PEM header), and any other key type is refused.
…tude block

An Amplitude credential holds a project's API key, secret key, and data residency region,
verified by listing the project's events on the region's Dashboard REST host with the Basic
key pair the tools send (Amplitude answers 403 for either key being invalid). The region and
key shapes are checked before any request. Each tool group maps only the params it declares:
analytics tools take both keys and the region, ingestion tools the API key and region, and
User Profile the secret key. The legacy block keeps its inline keys and is hidden from the
toolbar in favor of amplitude_v2.
…lock

A Kalshi credential holds an API key ID and its private key, verified by a free balance read
signed the way the tools sign every authenticated call; a key that cannot sign or a key ID that
would break the header is refused before any request. The tools that sign requests map the
key ID and private key from the credential, while market data stays public, so kalshi_v3
shows the credential picker only on account and trading operations. Token credential fields
gain an optional multiline flag so the connect form keeps a pasted PEM key's line breaks.
kalshi and kalshi_v2 keep their inline keys and now point at kalshi_v3.
…only Splunk block

A Splunk credential holds an authentication token and the management URL, verified by reading
authentication/current-context (documented, no capability needed) through the guarded egress
with the URL normalized as the tools build it; an answer without a user context is not taken as
proof, and certificate verification is never relaxed. The tools send the token whenever one is
set, so the credential policy marks the inline username and password as replaced by the token:
tokenCredentialOAuth gains an optional list of such params, and the saved-credential contract
accepts any token-filled param (not only accessToken) beside them. splunk keeps its inline
connection fields and points at splunk_v2.
…B Insights block

A CB Insights credential holds the client ID and secret, verified by exchanging them at
POST /v2/authorize, the call the tools make before their first request; a success without a
token is not taken as proof, and the error body is never logged. The tools keep minting and
caching their own token per run, so the credential fills the same client ID and secret they
take inline. The lookup's profileUrl is request data sent to api.cbinsights.com, recorded as
such in the saved-credential contract. cbinsights keeps its inline client fields and points at
cbinsights_v2.
The Kalshi signer is exercised by its tests and the CB Insights API base by the saved
credential check, so neither is unused any more.
… param other than accessToken

The saved-credential contract now accepts any credentialParams entry filled with the token beside
tokenReplacesParams (Splunk's authToken), and names that in its error.
…nly Railway block

A Railway credential holds a token, its type (account, workspace, or project), and for a
workspace token the workspace ID. Each type is verified with the read Railway documents for its
scope, since `me` answers only an account token: an account token reads `me`, a workspace token
reads its workspace by ID (passed as a GraphQL variable), and a project token reads
`projectToken` with the Project-Access-Token header the tools send for it. A GraphQL error or a
body without the queried object is not taken as proof. The tools receive the token and its type,
so a project token keeps its own header.

Token credential fields gain an optional flag: an optional field is left out of the required
create fields, the connect form does not require it and omits it when blank, and the secret
builder stores it only when set. Existing descriptors declare no optional field, so their
required fields and stored secrets are unchanged. railway keeps its inline token and points at
railway_v2.
… a credential-only Qdrant block

A Qdrant credential holds a database API key and the instance URL, verified by listing
collections (a read every key may run; a collection-scoped key sees a filtered list) with the
api-key header the tools send, through the guarded egress with the URL normalized as the tools
build it. An answer without a collection list is not taken as proof. The tools receive the key
and the verified URL, so a run reaches only that instance. qdrant keeps its inline URL and
optional key, which a self-hosted instance without an API key still needs, and points at
qdrant_v2.

The successor test matrix counts a field repeated per operation (Qdrant's URL) once.
…builder

The optional flag was honored by the create contract and connect form for any
field, but the secret builder only applied it to the workspace ID and still
required any domain field. Derive both the same way, and store a domain only
when it is required or supplied. No descriptor marks a domain optional today,
so stored secrets are unchanged.
Amplitude's User Profile API documents only Basic authentication with the
project API key and secret key. A saved Amplitude credential now fills the API
key too, and the tool sends Basic auth whenever an API key is present. Inline
calls that pass only the secret key keep the Api-Key header they sent before.
Drop the PKCS#1, query-string, literal newline and auth-header cases, which
restate unchanged behavior or repeat the validator's Ed25519 check.
@waleedlatif1
waleedlatif1 force-pushed the feat/saved-credentials-long-tail branch from 5dd438e to 9fbc7e5 Compare November 12, 2026 14:53
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 124 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1
waleedlatif1 merged commit ff166c1 into staging Nov 12, 2026
83 checks passed
@waleedlatif1
waleedlatif1 deleted the feat/saved-credentials-long-tail branch November 12, 2026 17:02

This branch was successfully deployed

1 active deployment
Preview — 9fbc7e5a Deployed Nov 12, 2026 by vercel[bot]
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.

1 participant