Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4b974a2
feat: add governance SDK functions — transferOwnership, scheduleUpgra…
crypt0fairy Mar 6, 2026
12e2770
feat: add CLI commands — app ownership transfer, upgrade schedule, up…
crypt0fairy Mar 6, 2026
764f38a
feat: rename governed→timelocked, add ownership/upgrade governance co…
crypt0fairy Mar 6, 2026
0b144a1
feat: add cancelUpgrade, team grant/revoke/list commands
crypt0fairy Mar 12, 2026
7801973
add command identity matrix
crypt0fairy Mar 12, 2026
5898936
feat: identity-centric auth — EOA/Safe/Timelock identities with activ…
crypt0fairy Mar 14, 2026
62efff3
feat: check for existing canonical Timelock before deploying in auth new
crypt0fairy Mar 14, 2026
9e20d85
fix: default Timelock proposer to signing key for EOA; prompt only fo…
crypt0fairy Mar 14, 2026
df38ffc
feat: safe identity flows, consistent warnings, show existing key bef…
crypt0fairy Mar 14, 2026
c230f0a
fix: read scheduled Release from chain event instead of rebuilding
crypt0fairy Mar 23, 2026
2cd50ad
test: add unit tests for getScheduledRelease
crypt0fairy Mar 23, 2026
55cf774
feat: add ecloud SDK and CLI support for timelocked governance operat…
crypt0fairy Mar 23, 2026
a2d92c3
docs: update identity command matrix with new governance commands and…
crypt0fairy Mar 24, 2026
402428c
refactor: split auth into keyring management + identity management
crypt0fairy Apr 10, 2026
021997b
docs: add identity limits and examples to auth flow map
crypt0fairy Apr 10, 2026
57ae775
feat: discover Timelock for both EOA and Safe proposers
crypt0fairy Apr 10, 2026
1ce68f8
docs: clarify Timelock CREATE2 determinism for both EOA and Safe
crypt0fairy Apr 10, 2026
395e930
feat: compute app list groups apps by identity, no private key required
crypt0fairy Apr 10, 2026
05ebcf0
feat: compute app list with identity grouping and API auth
crypt0fairy Apr 10, 2026
543f657
feat: identity-aware transaction routing for start/stop/terminate
crypt0fairy Apr 13, 2026
a8510ac
refactor: remove per-operation timelock commands, add identity routin…
crypt0fairy Apr 15, 2026
237fe9c
feat: use factory deployer registry for identity recovery on login
crypt0fairy Apr 15, 2026
908606c
fix: discover Safe-deployed Timelocks during login identity scan
crypt0fairy Apr 15, 2026
aab26c6
fix: use factory registry for identity discovery, fix whoami hints
crypt0fairy Apr 16, 2026
5ab224e
fix: allow deploying additional timelocks when one already exists
crypt0fairy Apr 16, 2026
85666b1
feat: support random salt for deploying multiple timelocks from same EOA
crypt0fairy Apr 16, 2026
7fa73f1
fix: use delay-derived deterministic salt instead of random
crypt0fairy Apr 16, 2026
c2e80f4
fix: require unit in delay input, validate at prompt
crypt0fairy Apr 16, 2026
574babb
fix: let runtime BUILD_TYPE env var override build-time constant
crypt0fairy Apr 16, 2026
8d28d8a
fix: show delay alongside address in existing timelock list and selector
crypt0fairy Apr 16, 2026
d5e0a4e
fix: update sepolia-dev AppController address after redeploy
crypt0fairy Apr 16, 2026
28489f4
feat: add auth sync command to rebuild identities from chain
crypt0fairy Apr 16, 2026
35056f9
fix: add identity creation hint to whoami output
crypt0fairy Apr 16, 2026
fc1479b
auth: identity UX overhaul — Safe/Timelock governance, sync command, …
crypt0fairy Apr 16, 2026
4fef5d6
feat: identity routing for team roles + lifecycle governance fixes
crypt0fairy Apr 17, 2026
201a547
feat: show pending Timelock ops in whoami + update AppController address
crypt0fairy Apr 17, 2026
7078024
fix: resolve Timelock(Safe) identity by checking PROPOSER_ROLE on-chain
crypt0fairy Apr 17, 2026
803f2ed
feat: add --execute and --cancel flags for Timelock operations
crypt0fairy Apr 20, 2026
9a4dbe7
feat: add --delay flag, improve op descriptions, fix arg validation
crypt0fairy Apr 20, 2026
38d2404
feat: declare active identity via X-eigenx-identity header
crypt0fairy Apr 23, 2026
e59ca38
Merge remote-tracking branch 'origin/master' into taras/gov
crypt0fairy Apr 23, 2026
a87a7ce
docs: remove obsolete governance working notes
crypt0fairy Apr 23, 2026
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
92 changes: 47 additions & 45 deletions packages/cli/src/commands/auth/generate.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
/**
* Auth Generate Command
*
* Generate a new private key and optionally store it in OS keyring
* Generate a new private key and optionally store it in OS keyring.
* This only manages the signing key — use `auth identity new` to create identities.
*/

import { Command, Flags } from "@oclif/core";
import { confirm } from "@inquirer/prompts";
import { generateNewPrivateKey, storePrivateKey, keyExists } from "@layr-labs/ecloud-sdk";
import {
generateNewPrivateKey,
storePrivateKey,
keyExists,
getPrivateKeyWithSource,
getAddressFromPrivateKey,
} from "@layr-labs/ecloud-sdk";
import { showPrivateKey, displayWarning } from "../../utils/security";
import { withTelemetry } from "../../telemetry";
import { replaceAllIdentities, setActiveIdentity } from "../../utils/globalConfig";

export default class AuthGenerate extends Command {
static description = "Generate a new private key";
static description = "Generate a new private key and store in OS keyring";

static aliases = ["auth:gen", "auth:new"];
static aliases = ["auth:gen"];

static examples = [
"<%= config.bin %> <%= command.id %>",
Expand All @@ -22,7 +30,7 @@ export default class AuthGenerate extends Command {

static flags = {
store: Flags.boolean({
description: "Automatically store in OS keyring",
description: "Automatically store in OS keyring (skip prompt)",
default: false,
}),
};
Expand All @@ -31,11 +39,36 @@ export default class AuthGenerate extends Command {
return withTelemetry(this, async () => {
const { flags } = await this.parse(AuthGenerate);

// Generate new key
this.log("Generating new private key...\n");
let shouldStore = flags.store;
if (!shouldStore) {
shouldStore = await confirm({ message: "Store this key in your OS keyring?", default: true });
}

// Check for existing key BEFORE generating a new one
if (shouldStore) {
const exists = await keyExists();
if (exists) {
const existing = await getPrivateKeyWithSource({ privateKey: undefined });
if (existing) {
const existingAddress = getAddressFromPrivateKey(existing.key);
displayWarning([
"A signing key already exists.",
`Address: ${existingAddress}`,
"",
"Replacing it will clear all current identities.",
]);
}
const confirmReplace = await confirm({ message: "Replace existing key?", default: false });
if (!confirmReplace) {
this.log("\nCancelled.");
return;
}
}
}

// Generate the new key
const { privateKey, address } = generateNewPrivateKey();

// Display key securely
const content = `
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
A new private key was generated for you.
Expand All @@ -56,54 +89,23 @@ Press 'q' to exit and continue...
`;

const displayed = await showPrivateKey(content);

if (!displayed) {
this.log("Key generation cancelled.");
return;
}

// Ask about storing
let shouldStore = flags.store;

if (!shouldStore && displayed) {
shouldStore = await confirm({
message: "Store this key in your OS keyring?",
default: true,
});
}

if (shouldStore) {
// Check if key already exists
const exists = await keyExists();

if (exists) {
displayWarning([
`WARNING: A private key for ecloud already exists!`,
"If you continue, the existing key will be PERMANENTLY REPLACED.",
"This cannot be undone!",
"",
"The previous key will be lost forever if you haven't backed it up.",
]);

const confirmReplace = await confirm({
message: `Replace existing key for ecloud?`,
default: false,
});

if (!confirmReplace) {
this.log(
"\nKey not stored. If you did not save your new key when it was displayed, it is now lost and cannot be recovered.",
);
return;
}
}

// Store the key
try {
await storePrivateKey(privateKey);
// New signing key — wipe all identities (they belonged to the previous key)
replaceAllIdentities([{ type: "eoa", address }]);
for (const env of ["sepolia", "sepolia-dev", "mainnet-alpha"]) {
setActiveIdentity(env, address);
}
this.log(`\n✓ Private key stored in OS keyring`);
this.log(`✓ Address: ${address}`);
this.log("\nYou can now use ecloud commands without --private-key flag.");
this.log("Run 'ecloud auth identity new' to create a Safe or Timelock identity.");
} catch (err: any) {
this.error(`Failed to store key: ${err.message}`);
}
Expand Down
53 changes: 53 additions & 0 deletions packages/cli/src/commands/auth/identity/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Auth Identity List Command
*
* Show all stored identities and which is active.
*/

import { Command } from "@oclif/core";
import { withTelemetry } from "../../../telemetry";
import { commonFlags } from "../../../flags";
import {
getIdentities,
getActiveIdentityAddress,
formatIdentity,
} from "../../../utils/globalConfig";

export default class AuthIdentityList extends Command {
static description = "Show all stored identities";

static aliases = ["auth:identity:ls"];

static examples = ["<%= config.bin %> <%= command.id %>"];

static flags = {
environment: commonFlags.environment,
};

async run(): Promise<void> {
return withTelemetry(this, async () => {
const { flags } = await this.parse(AuthIdentityList);
const environment = flags.environment as string;

const identities = getIdentities();
const activeAddress = getActiveIdentityAddress(environment);

if (identities.length === 0) {
this.log("No identities.");
this.log("\nRun 'ecloud auth identity new' to create one.");
return;
}

this.log(`Identities (${environment}):\n`);
for (const id of identities) {
const isActive = id.address.toLowerCase() === activeAddress?.toLowerCase();
const marker = isActive ? "●" : "○";
const active = isActive ? " ← active" : "";
this.log(` ${marker} ${formatIdentity(id)}${active}`);
}

this.log("");
this.log("Run 'ecloud auth identity select' to switch active identity.");
});
}
}
Loading
Loading