Skip to main content

The sf CLI handoff

vulkro-sf does not implement its own OAuth flow against Salesforce. Every live-org subcommand (org status, org perms, org packages) hands off to the official Salesforce CLI (sf) for the credential side, then performs the Metadata + Tooling API call directly against your org with the short-lived access token sf returns. The token never persists on disk in Vulkro's storage, and it is discarded from memory the moment the API call completes.

This page documents exactly what happens at each hop, what vulkro-sf reads, what it never reads, and how to revoke access.

Why we do not manage OAuth ourselves

The sf CLI is the official Salesforce credential store. Salesforce audits it, ships security updates against it, and trust-roots it at the platform level. Specifically:

  • The OAuth refresh-token grant is stored in the OS-native secure keychain (macOS Keychain, Windows DPAPI, Linux libsecret), not in plain text on disk.
  • The access-token refresh dance follows Salesforce's documented rotation policy and respects revocations issued through Setup.
  • Multi-org session handling, sandbox vs production endpoint routing, and instance-URL changes are all problems Salesforce already solved.

Reimplementing this layer in vulkro-sf would be a strict downgrade: more code to audit, a second credential store for the user to revoke through, and a second blast radius in the event of a compromise. The handoff to sf is the right architectural decision and the privacy property comes for free.

What happens when you run vulkro-sf org status

Walking the call chain end-to-end, for the alias my-prod:

  1. You invoke vulkro-sf org status --target-org my-prod.
  2. vulkro-sf shells out to sf with a token-fetch command against the named alias. The sf CLI refreshes the access token if needed (consulting its OAuth refresh-token in the keychain), then returns a short-lived access token and the instance URL.
  3. vulkro-sf calls the Metadata + Tooling API at the instance URL with that access token. The fetch is targeted: a finite set of Metadata API queries (Profile, PermissionSet, SecuritySettings, ConnectedApplication, NamedCredential, etc.) plus Tooling API queries for the entitities the Metadata API does not expose (ApexClass.Body, Flow.Definition, installed package state).
  4. The detectors run against the in-memory metadata payload. No intermediate file is written to disk; the response object is held only for the duration of the scan.
  5. The token is discarded from vulkro-sf's memory when the scan exits. No persistence step ever writes the token to ~/.vulkro/, to the SQLite findings store, or to any log file.

What gets read at runtime

The complete list of API calls vulkro-sf org may issue. Every entry is the Metadata API or Tooling API. No SOQL Query API call against business records. No Bulk API call. No SELECT against any sObject that contains customer data.

Metadata API queries

  • Profile (with objectPermissions, userPermissions, fieldPermissions, license-type derivation).
  • PermissionSet (same shape).
  • PermissionSetGroup.
  • SecuritySettings (session timeout, clickjack, CSRF, HTTPS, password policy, login IP ranges).
  • ConnectedApplication (OAuth scopes, callback URL, refresh-token policy, consumer key).
  • NamedCredential and ExternalCredential.
  • RemoteSiteSetting, CorsWhitelistOrigin, CspTrustedSite.
  • AuthProvider, SamlSsoConfig, LoginFlow, LoginIpRange.
  • Network (Experience Cloud), Site (Force.com Sites), ExperienceBundle.
  • Flow, WorkflowRule, WorkflowOutboundMessage.
  • StaticResource (file metadata only; the body fetch is opt-in).
  • Certificate (key size, algorithm; private-key material itself stays on the server).

Tooling API queries

  • ApexClass (for sharing-keyword and CRUD/FLS posture).
  • ApexTrigger.
  • InstalledSubscriberPackage, SubscriberPackage, SubscriberPackageVersion.
  • GenAiFunction, GenAiPlanner, GenAiPromptTemplate (for Agentforce posture).
  • User (System Administrator subset only: Username, LastLoginDate, IsActive, Profile.Name for the dormant-admin check; no email body, no phone number, no PII fields).

That is the complete read surface. The connector emits zero SELECT against any sObject containing customer business data.

Token revocation

The OAuth grant vulkro-sf uses is the same grant sf itself uses. Revoking access is the standard Salesforce CLI revocation flow:

  1. Setup -> Apps -> Connected Apps OAuth Usage.
  2. Locate Salesforce CLI in the list.
  3. Click Revoke (or Block).

Once revoked, both vulkro-sf org * and any other sf command against that org return an authentication error on the next call. To resume, re-run sf org login web --alias my-prod.

Revocation is also the right answer if a laptop is lost or stolen: the OS keychain holds the refresh token; revoking on the Salesforce side invalidates it everywhere, even if the device is later recovered.

Multi-org workflows

sf org list shows every org sf has a token for, with the alias and default flag. vulkro-sf accepts any of those aliases as --target-org:

sf org list
# > ALIAS USERNAME ORG ID ...
# > my-prod [email protected] 00D5g000004XXXXAAA ...
# > my-sandbox [email protected] 00D2y000007YYYYAAA ...
# > my-scratch [email protected] 00DRm0000018ZZZZAAA ...

vulkro-sf org status --target-org my-prod
vulkro-sf org perms --target-org my-sandbox
vulkro-sf org packages --target-org my-scratch

The aliases are local to each laptop's sf configuration. A CI runner authenticates separately (see GitHub Actions and GitLab CI for the JWT-grant pattern that fits CI).

What this buys the user

Three properties that fall out of the handoff design:

  1. Single revocation surface. One Setup page revokes both the sf CLI and vulkro-sf simultaneously. No second "where does Vulkro store credentials?" question to answer.
  2. OS-native at-rest encryption. The refresh token lives in the keychain Salesforce itself recommends; vulkro-sf adds no second store with different threat properties.
  3. Audit-trail consistency. Every API call from vulkro-sf shows up in the org's Setup Audit Trail under the same Salesforce CLI entry an admin already knows how to interpret. No new agent identity to triage.

For the broader privacy contract (the full "what is read / what is not" wording, the egress profile, the air-gapped property), see Live-org setup.