Constellation API
The constellation() function is the main SDK entry point. It returns an API
for declaring account constellations — the Safes, Roles modules, and users that
make up your onchain setup.
import { constellation } from '@zodiac-os/sdk'Scoping to a workspace and chain
Each constellation is scoped to a single workspace and chain. The workspace
option must be a valid workspace name from your org.
const eth = constellation({
workspace: 'GG',
label: 'Production',
chain: 1,
})After zodiac pull-org, the workspace name, chain id, and account/role/user
labels all autocomplete and type-check against your real org.
Referencing existing accounts
Bracket access gives you existing Safes and Roles modules from the selected
workspace — both vault accounts surfaced in the workspace UI and any
constellation accounts previously created by a push().
// Reference an existing Safe — no invocation needed
const ggDao = eth.safe['GG DAO']
// Reference an existing Roles mod
const ggDaoRoles = eth.roles['GG DAO Roles']
// Optionally invoke with overrides
const ggDaoOverridden = eth.safe['GG DAO']({ threshold: 5 })When a bracket label matches an existing account, all overrides become optional — you pass only the fields you want to change against the live config.
Creating new accounts
Use bracket access with a new label to create new nodes. Every mandatory field must be supplied explicitly — the SDK injects no runtime defaults, so a missing field is a compile-time error.
// New Safe — nonce, threshold, owners are required
const newSafe = eth.safe['New Safe']({
nonce: 0n,
threshold: 2,
owners: [
eth.user['Alice Sample'],
'0xb8e48df6818d3cbc648b3e8ec248a4f547135f7a',
],
modules: [ggDaoRoles],
})
// New Roles mod targeting an existing Safe
const newRoles = eth.roles['New Roles']({
nonce: 0n,
target: ggDao,
})A Roles node takes either nonce (deploy a new module) or address
(bind to a module already deployed onchain but not in your workspace) — never
both. Binding by address makes Zodiac diff your spec against the live module
and update only what changed.
Forward references between new nodes
New nodes can reference each other before either has been invoked — the
uninvoked factory works as a forward reference. References resolve by label at
push() time, so both sides of the cycle must be in the call.
const safe = eth.safe['New Safe']({
nonce: 0n,
threshold: 1,
owners: [eth.user['Alice Sample']],
modules: [eth.roles['New Roles']], // forward ref
})
const roles = eth.roles['New Roles']({
nonce: 0n,
target: safe,
})Referencing users
eth.user[handle] resolves a user to their personal Safe address on the
current chain — useful in owner lists and role-member arrays:
const aliceAddress = eth.user['Alice Sample']Pushing
push() takes all nodes and sends them to the Zodiac OS API. Pass either a
named object (keys become refs) or an array. All referenced nodes must be
included.
import { push } from '@zodiac-os/sdk'
await push({ ggDao, ggDaoRoles, newSafe, newRoles })By default push() builds an API client from the ZODIAC_API_KEY environment
variable. Pass a custom client to override:
await push({ ggDao, newRoles }, { api: new ApiClient({ apiKey: '...' }) })In the template, bun push wraps this call and opens the browser to review the
diff and sign. See Permissions & the allow kit for scoping
what a role can actually call.