Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ onecli secrets update --id X --value Y Update a secret
onecli secrets delete --id X Delete a secret
```

### Rules
### Rules (legacy model)

Cloud deployments reject these writes (410) — use the `policy` family below.
Pre-cutover self-hosted servers still accept them.

```
onecli rules list List all policy rules
Expand All @@ -58,14 +61,36 @@ onecli rules update --id X [--action block] ... Update a rule
onecli rules delete --id X Delete a rule
```

### Policy (the policy engine)

Rules stage into a DRAFT and enforce on publish. Writes auto-publish when the
draft has no other staged changes (`--no-publish` stages; `--publish-all`
publishes everything).

```
onecli policy rules list [--status published] List rules (draft or the enforced set)
onecli policy rules get --id X Get a DRAFT rule
onecli policy rules create --name X --action allow \
--targets '[{"kind":"network","hostPattern":"api.example.com"}]'
onecli policy rules update --id X [--action block] Update a DRAFT rule
onecli policy rules delete --id X Delete a DRAFT rule
onecli policy rules reorder --ordered-ids '[...]' Reorder (every draft id exactly once)
onecli policy default get Show the terminal Default Rule
onecli policy default set --action allow|block Set the Default Rule's action
onecli policy publish Publish the whole staged draft
onecli policy status Staged diff + last publish
```

### Organization

Organization-level resources are shared by every project in the org. Authenticate with an organization API key (`oc_org_...`); project selection is not required.

```
onecli org secrets list|create|update|delete Manage org-level secrets
onecli org rules list|get|create|update|delete Manage org-level rules
onecli org rules permissions get|set --provider X Layered app permissions
onecli org rules list|get|create|update|delete Manage org-level rules (legacy; cloud rejects writes)
onecli org rules permissions get|set --provider X Layered app permissions (legacy; cloud rejects writes)
onecli org policy rules list|get|create|update|delete|reorder Org policy-engine rules (draft → publish)
onecli org policy default get|set / publish / status Org Default Rule + publish + staged diff
onecli org connections list [--provider X] List org connections
onecli org connections rename --id X --label Y Rename an org connection
onecli org connections delete --id X Delete an org connection
Expand Down
80 changes: 72 additions & 8 deletions cmd/onecli/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,24 +164,24 @@ func (cmd *HelpCmd) Run(out *output.Writer) error {
{Name: "rules list", Description: "List all policy rules.", Args: []ArgInfo{
{Name: "--project, -p", Description: "Project slug."},
}},
{Name: "rules create", Description: "Create a new policy rule.", Args: []ArgInfo{
{Name: "rules create", Description: "Create a legacy rule (cloud deployments reject with 410 — use 'policy rules create').", Args: []ArgInfo{
{Name: "--project, -p", Description: "Project slug."},
{Name: "--name", Required: true, Description: "Display name for the rule."},
{Name: "--host-pattern", Required: true, Description: "Host pattern to match."},
{Name: "--action", Required: true, Description: "Action: 'block', 'rate_limit', 'manual_approval', or 'allow'."},
{Name: "--conditions", Description: "Content conditions as a JSON array."},
}},
{Name: "rules update", Description: "Update an existing policy rule.", Args: []ArgInfo{
{Name: "rules update", Description: "Update a legacy rule (cloud deployments reject with 410 — use 'policy rules update').", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to update."},
}},
{Name: "rules delete", Description: "Delete a policy rule.", Args: []ArgInfo{
{Name: "rules delete", Description: "Delete a legacy rule (cloud deployments reject with 410 — use 'policy rules delete').", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to delete."},
}},
{Name: "rules permissions get", Description: "Get layered tool permissions for a provider.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
{Name: "--agent-id", Description: "Show only this agent's override layer."},
}},
{Name: "rules permissions set", Description: "Set tool permissions for a provider (optionally per agent).", Args: []ArgInfo{
{Name: "rules permissions set", Description: "Set tool permissions (legacy — cloud deployments reject with 410; use 'policy' rules with app targets).", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
{Name: "--tool", Description: "Tool ID (see 'apps permission-definition')."},
{Name: "--permission", Description: "Permission: 'allow', 'manual_approval', 'block', or 'inherit' (agent layer only)."},
Expand All @@ -191,6 +191,41 @@ func (cmd *HelpCmd) Run(out *output.Writer) error {
{Name: "rules overlap", Description: "Count custom rules overlapping an app's hosts.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name."},
}},
{Name: "policy rules list", Description: "List policy-engine rules (draft or the enforced published set).", Args: []ArgInfo{
{Name: "--project, -p", Description: "Project slug."},
{Name: "--status", Description: "'draft' (default) or 'published' (enforced)."},
}},
{Name: "policy rules get", Description: "Get one DRAFT policy rule by id.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "Draft rule id (published ids regenerate every publish — match by logicalId)."},
}},
{Name: "policy rules create", Description: "Create a policy rule (auto-publishes when the draft is otherwise clean).", Args: []ArgInfo{
{Name: "--name", Description: "Display name (required unless --json)."},
{Name: "--action", Description: "'allow' or 'block' (required unless --json)."},
{Name: "--targets", Description: "JSON array of targets: app/connection/secret/network (required unless --json)."},
{Name: "--identities", Description: "JSON array of identities; omit for all agents."},
{Name: "--rate-limit", Description: "Max requests per window (allow rules; pair with --rate-limit-window)."},
{Name: "--require-approval", Description: "Require manual approval (allow rules)."},
{Name: "--json", Description: "Raw JSON payload for the full rule (do not combine with field flags)."},
{Name: "--no-publish", Description: "Stage only."},
{Name: "--publish-all", Description: "Publish even when the draft holds other staged changes."},
}},
{Name: "policy rules update", Description: "Update a DRAFT policy rule (same publish flags as create).", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "Draft rule id."},
}},
{Name: "policy rules delete", Description: "Delete a DRAFT policy rule (same publish flags).", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "Draft rule id."},
}},
{Name: "policy rules reorder", Description: "Reorder the draft — the id list must name EVERY non-default draft rule.", Args: []ArgInfo{
{Name: "--ordered-ids", Required: true, Description: "JSON array of all draft rule ids (from 'policy rules list --quiet id')."},
}},
{Name: "policy default get", Description: "Show the terminal Default Rule.", Args: []ArgInfo{
{Name: "--status", Description: "'draft' (default) or 'published'."},
}},
{Name: "policy default set", Description: "Set the Default Rule's action.", Args: []ArgInfo{
{Name: "--action", Required: true, Description: "'allow' or 'block'."},
}},
{Name: "policy publish", Description: "Publish the WHOLE staged draft (all staged changes, yours and others')."},
{Name: "policy status", Description: "Show staged changes (the diff) and the last publish."},
{Name: "projects list", Description: "List all projects."},
{Name: "projects get", Description: "Get a single project by ID.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the project to retrieve."},
Expand Down Expand Up @@ -222,27 +257,56 @@ func (cmd *HelpCmd) Run(out *output.Writer) error {
{Name: "org rules get", Description: "Get a single org-scoped policy rule.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to retrieve."},
}},
{Name: "org rules create", Description: "Create a new org-scoped policy rule.", Args: []ArgInfo{
{Name: "org rules create", Description: "Create a legacy org rule (cloud deployments reject with 410 — use 'org policy rules create').", Args: []ArgInfo{
{Name: "--name", Required: true, Description: "Display name for the rule."},
{Name: "--host-pattern", Required: true, Description: "Host pattern to match."},
{Name: "--action", Required: true, Description: "Action: 'block', 'rate_limit', 'manual_approval', or 'allow'."},
}},
{Name: "org rules update", Description: "Update an org-scoped policy rule.", Args: []ArgInfo{
{Name: "org rules update", Description: "Update a legacy org rule (cloud deployments reject with 410 — use 'org policy rules update').", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to update."},
}},
{Name: "org rules delete", Description: "Delete an org-scoped policy rule.", Args: []ArgInfo{
{Name: "org rules delete", Description: "Delete a legacy org rule (cloud deployments reject with 410 — use 'org policy rules delete').", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to delete."},
}},
{Name: "org rules permissions get", Description: "Get tool permissions for a provider.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
}},
{Name: "org rules permissions set", Description: "Set tool permissions for a provider.", Args: []ArgInfo{
{Name: "org rules permissions set", Description: "Set org tool permissions (legacy — cloud deployments reject with 410; use 'org policy' rules with app targets).", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
{Name: "--json", Required: true, Description: "JSON payload with 'changes' array."},
}},
{Name: "org rules overlap", Description: "Count custom org rules overlapping an app's hosts.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name."},
}},
{Name: "org policy rules list", Description: "List org policy-engine rules (draft or published).", Args: []ArgInfo{
{Name: "--status", Description: "'draft' (default) or 'published' (enforced)."},
}},
{Name: "org policy rules get", Description: "Get one DRAFT org policy rule by id.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "Draft rule id (published ids regenerate every publish — match by logicalId)."},
}},
{Name: "org policy rules create", Description: "Create an org policy rule (group/user identities; auto-publishes when the draft is clean).", Args: []ArgInfo{
{Name: "--name", Description: "Display name (required unless --json)."},
{Name: "--action", Description: "'allow' or 'block' (required unless --json)."},
{Name: "--targets", Description: "JSON array of targets (required unless --json)."},
{Name: "--identities", Description: "JSON array — org rules take agentGroup/user/group identities."},
{Name: "--no-publish", Description: "Stage only."},
{Name: "--publish-all", Description: "Publish even when the draft holds other staged changes."},
}},
{Name: "org policy rules update", Description: "Update a DRAFT org policy rule.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "Draft rule id."},
}},
{Name: "org policy rules delete", Description: "Delete a DRAFT org policy rule.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "Draft rule id."},
}},
{Name: "org policy rules reorder", Description: "Reorder the org draft (full id permutation).", Args: []ArgInfo{
{Name: "--ordered-ids", Required: true, Description: "JSON array of all org draft rule ids."},
}},
{Name: "org policy default get", Description: "Show the org's terminal Default Rule."},
{Name: "org policy default set", Description: "Set the org Default Rule's action.", Args: []ArgInfo{
{Name: "--action", Required: true, Description: "'allow' or 'block'."},
}},
{Name: "org policy publish", Description: "Publish the org's WHOLE staged draft."},
{Name: "org policy status", Description: "Show the org's staged changes and last publish."},
{Name: "org connections list", Description: "List all org-scoped connections.", Args: []ArgInfo{
{Name: "--provider", Description: "Filter by provider name."},
}},
Expand Down
38 changes: 36 additions & 2 deletions cmd/onecli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ type CLI struct {
Agents AgentsCmd `cmd:"" help:"Manage agents."`
Secrets SecretsCmd `cmd:"" help:"Manage secrets."`
Apps AppsCmd `cmd:"" help:"Manage app connections."`
Rules RulesCmd `cmd:"" help:"Manage policy rules."`
Rules RulesCmd `cmd:"" help:"Manage legacy policy rules (cloud deployments reject writes — see 'onecli policy')."`
Policy PolicyCmd `cmd:"" help:"Manage policy rules on the policy engine (draft → publish)."`
Projects ProjectsCmd `cmd:"" help:"Manage projects."`
Org OrgCmd `cmd:"" help:"Organization-scoped management (secrets, rules, connections, apps, settings)."`
Org OrgCmd `cmd:"" help:"Organization-scoped management (secrets, rules, policy, connections, apps, settings)."`
Vaults VaultsCmd `cmd:"" help:"List external vault connections."`
Counts CountsCmd `cmd:"" help:"Show the project's resource counts."`
Auth AuthCmd `cmd:"" help:"Manage authentication."`
Expand Down Expand Up @@ -83,6 +84,17 @@ func main() {
func handleError(out *output.Writer, err error) {
var apiErr *api.APIError
if errors.As(err, &apiErr) {
// A 400/401 demanding a project header is a scoping problem, not an
// auth one — "onecli auth login" would be misleading advice.
if (apiErr.StatusCode == 400 || apiErr.StatusCode == 401) &&
strings.Contains(apiErr.Message, "X-Project-Id") {
_ = out.ErrorWithAction(
exitcode.CodeError,
apiErr.Message,
"pass --project <slug> or run 'onecli config set project <slug>'",
)
os.Exit(exitcode.Error)
}
switch apiErr.StatusCode {
case 401:
_ = out.ErrorWithAction(exitcode.CodeAuthRequired, apiErr.Message, "onecli auth login")
Expand All @@ -96,13 +108,35 @@ func handleError(out *output.Writer, err error) {
case 409:
_ = out.Error(exitcode.CodeConflict, apiErr.Message)
os.Exit(exitcode.Conflict)
case 410:
// A retired endpoint: the server message names the replacement.
_ = out.ErrorWithAction(
exitcode.CodeGone,
apiErr.Message,
"onecli policy --help (org rules: 'onecli org policy --help')",
)
os.Exit(exitcode.Error)
case 422:
_ = out.Error(exitcode.CodeValidation, apiErr.Message)
os.Exit(exitcode.Error)
}
}

_ = out.Error(exitcode.CodeError, err.Error())
os.Exit(exitcode.Error)
}

// loadStoredAPIKey returns the resolved API key (env or credential file),
// or "" — used for fail-fast key-shape checks; the client loads it itself.
func loadStoredAPIKey() string {
credDir, err := config.CredentialsDir()
if err != nil {
return ""
}
key, _ := auth.NewStore(nil, credDir).Load()
return key
}

// newClient creates an API client using the resolved API key and host.
// If no API key is stored, the client is created without one — the server
// decides whether authentication is required (local mode doesn't need it).
Expand Down
3 changes: 2 additions & 1 deletion cmd/onecli/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package main
// OrgCmd is the `onecli org` command group for organization-scoped operations.
type OrgCmd struct {
Secrets OrgSecretsCmd `cmd:"" help:"Manage org-scoped secrets."`
Rules OrgRulesCmd `cmd:"" help:"Manage org-scoped policy rules."`
Rules OrgRulesCmd `cmd:"" help:"Manage legacy org policy rules (cloud deployments reject writes — see 'onecli org policy')."`
Policy OrgPolicyCmd `cmd:"" help:"Manage org policy rules on the policy engine (draft → publish)."`
Connections OrgConnectionsCmd `cmd:"" help:"Manage org-scoped connections."`
Apps OrgAppsCmd `cmd:"" help:"Manage org-scoped app configuration."`
Settings OrgSettingsCmd `cmd:"" help:"Manage organization settings (policy mode)."`
Expand Down
Loading
Loading