platform.apiKey becomes platform.gadgetKey

gadget-drone now presents an ApiClient _id value as the Gadget Key,
allowing gadget-code to reference the client, determine the associated
User, and invoke logic on the User's behalf as an authenticated and
authorized client.
This commit is contained in:
Rob Colbert 2026-05-05 08:12:34 -04:00
parent 7b4e122533
commit 8333672683
7 changed files with 44 additions and 43 deletions

View File

@ -302,6 +302,7 @@ gadget-drone
``` ```
Each drone will: Each drone will:
- Have its own unique workspace ID (stored in `.gadget/workspace.json`) - Have its own unique workspace ID (stored in `.gadget/workspace.json`)
- Register separately with the platform - Register separately with the platform
- Process work orders independently - Process work orders independently
@ -321,14 +322,14 @@ Copy your `.env` values to the appropriate YAML file using the examples above.
Map your old `.env` variable names to the new YAML structure: Map your old `.env` variable names to the new YAML structure:
| Old .env Variable | New YAML Path | | Old .env Variable | New YAML Path |
|-------------------|---------------| | ------------------------ | -------------------- |
| `DTP_JWT_SECRET` | `auth.jwtSecret` | | `DTP_JWT_SECRET` | `auth.jwtSecret` |
| `DTP_USER_PASSWORD_SALT` | `auth.passwordSalt` | | `DTP_USER_PASSWORD_SALT` | `auth.passwordSalt` |
| `DTP_SESSION_SECRET` | `session.secret` | | `DTP_SESSION_SECRET` | `session.secret` |
| `DTP_MONGODB_HOST` | `mongodb.host` | | `DTP_MONGODB_HOST` | `mongodb.host` |
| `DTP_REDIS_HOST` | `redis.host` | | `DTP_REDIS_HOST` | `redis.host` |
| `GADGET_PLATFORM_URL` | `platform.baseUrl` | | `GADGET_PLATFORM_URL` | `platform.baseUrl` |
| `GADGET_PLATFORM_KEY` | `platform.apiKey` | | `GADGET_PLATFORM_KEY` | `platform.gadgetKey` |
### 3. Set Environment Variables ### 3. Set Environment Variables
@ -419,11 +420,3 @@ node -e "console.log(require('js-yaml').load(require('fs').readFileSync('~/.conf
3. **Use HTTPS in production**: Always enable HTTPS for the web server 3. **Use HTTPS in production**: Always enable HTTPS for the web server
4. **Restrict database access**: Configure MongoDB and Redis to only accept local connections 4. **Restrict database access**: Configure MongoDB and Redis to only accept local connections
5. **Rotate secrets regularly**: Update JWT secrets, password salts, and API keys periodically 5. **Rotate secrets regularly**: Update JWT secrets, password salts, and API keys periodically
## Support
For issues or questions:
- **Documentation**: https://github.com/anomalyco/gadget/tree/main/docs
- **Issues**: https://github.com/anomalyco/gadget/issues
- **Discussions**: https://github.com/anomalyco/gadget/discussions

View File

@ -19,19 +19,19 @@ const yamlConfig = loadGadgetCodeConfig();
if (!yamlConfig.auth?.jwtSecret) { if (!yamlConfig.auth?.jwtSecret) {
throw new Error( throw new Error(
"Configuration error: auth.jwtSecret is required in gadget-code.yaml\n" + "Configuration error: auth.jwtSecret is required in gadget-code.yaml\n" +
"See documentation: https://github.com/anomalyco/gadget/blob/main/docs/configuration.md", "See documentation: ./docs/configuration.md",
); );
} }
if (!yamlConfig.auth?.passwordSalt) { if (!yamlConfig.auth?.passwordSalt) {
throw new Error( throw new Error(
"Configuration error: auth.passwordSalt is required in gadget-code.yaml\n" + "Configuration error: auth.passwordSalt is required in gadget-code.yaml\n" +
"See documentation: https://github.com/anomalyco/gadget/blob/main/docs/configuration.md", "See documentation: ./docs/configuration.md",
); );
} }
if (!yamlConfig.session?.secret) { if (!yamlConfig.session?.secret) {
throw new Error( throw new Error(
"Configuration error: session.secret is required in gadget-code.yaml\n" + "Configuration error: session.secret is required in gadget-code.yaml\n" +
"See documentation: https://github.com/anomalyco/gadget/blob/main/docs/configuration.md", "See documentation: ./docs/configuration.md",
); );
} }
@ -66,7 +66,8 @@ export default {
companyShort: yamlConfig.site?.companyShort || "Colbert", companyShort: yamlConfig.site?.companyShort || "Colbert",
name: yamlConfig.site?.name || "Gadget Code", name: yamlConfig.site?.name || "Gadget Code",
shortName: yamlConfig.site?.shortName || "Gadget Code", shortName: yamlConfig.site?.shortName || "Gadget Code",
slogan: yamlConfig.site?.slogan || "Self-hosted Agentic Engineering Platform", slogan:
yamlConfig.site?.slogan || "Self-hosted Agentic Engineering Platform",
description: description:
yamlConfig.site?.description || yamlConfig.site?.description ||
"Gadget Code - A self-hosted Agentic Engineering Platform (AEP).", "Gadget Code - A self-hosted Agentic Engineering Platform (AEP).",

View File

@ -190,7 +190,13 @@ class DtpWebCli extends DtpProcess {
} }
printApiClientList(clients: IApiClient[]) { printApiClientList(clients: IApiClient[]) {
console.log("Name".padEnd(20), "Client ID".padEnd(24), "Secret"); console.log(
"Name".padEnd(20),
"Gadget Key".padEnd(21),
"Secret".padEnd(36),
"User ID".padEnd(21),
"Email",
);
console.log( console.log(
"--------------------------------------------------------------------------------", "--------------------------------------------------------------------------------",
); );

View File

@ -19,13 +19,13 @@ const yamlConfig = loadGadgetDroneConfig();
if (!yamlConfig.platform?.baseUrl) { if (!yamlConfig.platform?.baseUrl) {
throw new Error( throw new Error(
"Configuration error: platform.baseUrl is required in gadget-drone.yaml\n" + "Configuration error: platform.baseUrl is required in gadget-drone.yaml\n" +
"See documentation: https://github.com/anomalyco/gadget/blob/main/docs/configuration.md", "See documentation: ./docs/configuration.md",
); );
} }
if (!yamlConfig.platform?.apiKey) { if (!yamlConfig.platform?.gadgetKey) {
throw new Error( throw new Error(
"Configuration error: platform.apiKey is required in gadget-drone.yaml\n" + "Configuration error: platform.gadgetKey is required in gadget-drone.yaml\n" +
"See documentation: https://github.com/anomalyco/gadget/blob/main/docs/configuration.md", "See documentation: ./docs/configuration.md",
); );
} }
@ -45,7 +45,7 @@ export default {
), ),
platform: { platform: {
baseUrl: yamlConfig.platform.baseUrl, baseUrl: yamlConfig.platform.baseUrl,
apiKey: yamlConfig.platform.apiKey, gadgetKey: yamlConfig.platform.gadgetKey,
}, },
log: { log: {
console: { console: {

View File

@ -74,7 +74,7 @@ class PlatformService extends GadgetService {
Accept: "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
"Content-Length": body.length.toString(), "Content-Length": body.length.toString(),
"X-Gadget-Key": env.platform.apiKey, "X-Gadget-Key": env.platform.gadgetKey,
}, },
body, body,
}); });
@ -122,7 +122,7 @@ class PlatformService extends GadgetService {
Accept: "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
"Content-Length": body.length.toString(), "Content-Length": body.length.toString(),
"X-Gadget-Key": env.platform.apiKey, "X-Gadget-Key": env.platform.gadgetKey,
}, },
body, body,
}); });
@ -152,7 +152,7 @@ class PlatformService extends GadgetService {
Accept: "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
"Content-Length": body.length.toString(), "Content-Length": body.length.toString(),
"X-Gadget-Key": env.platform.apiKey, "X-Gadget-Key": env.platform.gadgetKey,
}, },
body, body,
}); });
@ -181,7 +181,7 @@ class PlatformService extends GadgetService {
method: "GET", method: "GET",
headers: { headers: {
Accept: "application/json", Accept: "application/json",
"X-Gadget-Key": env.platform.apiKey, "X-Gadget-Key": env.platform.gadgetKey,
}, },
}); });

View File

@ -50,9 +50,10 @@ function processConfigValues(obj: unknown): unknown {
* Search for configuration file in standard locations. * Search for configuration file in standard locations.
* Order: ~/.config/gadget/ first, then /etc/gadget/ * Order: ~/.config/gadget/ first, then /etc/gadget/
*/ */
export function searchConfigFile( export function searchConfigFile(configName: string): {
configName: string, path: string;
): { path: string; exists: boolean } { exists: boolean;
} {
const homeConfigDir = path.join(os.homedir(), ".config", "gadget"); const homeConfigDir = path.join(os.homedir(), ".config", "gadget");
const systemConfigDir = "/etc/gadget"; const systemConfigDir = "/etc/gadget";
@ -87,7 +88,7 @@ export function loadYamlConfig<T>(configName: string): T {
` - ${path.join(os.homedir(), ".config", "gadget", configName)}\n` + ` - ${path.join(os.homedir(), ".config", "gadget", configName)}\n` +
` - /etc/gadget/${configName}\n\n` + ` - /etc/gadget/${configName}\n\n` +
`Please create a configuration file. See documentation at:\n` + `Please create a configuration file. See documentation at:\n` +
` https://github.com/anomalyco/gadget/blob/main/docs/configuration.md`, ` ./docs/configuration.md`,
); );
error.name = "ConfigNotFoundError"; error.name = "ConfigNotFoundError";
throw error; throw error;

View File

@ -118,7 +118,7 @@ export interface GadgetDroneConfig {
timezone?: string; timezone?: string;
platform: { platform: {
baseUrl: string; baseUrl: string;
apiKey: string; gadgetKey: string;
}; };
logging?: { logging?: {
console?: { console?: {