@descix/cli 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/LICENSE.md +671 -0
  2. package/README.md +43 -4
  3. package/agent-assets/guides/README.md +2 -2
  4. package/agent-assets/guides/dynamic-site-pattern.md +29 -14
  5. package/agent-assets/guides/knowledge-only-pattern.md +5 -5
  6. package/agent-assets/guides/microservice-pattern.md +48 -4
  7. package/agent-assets/guides/static-site-pattern.md +8 -0
  8. package/agent-assets/instructions/app-asset-validation.md +5 -5
  9. package/agent-assets/instructions/onboarding-flow.md +4 -4
  10. package/agent-assets/instructions/standard-folder-structure.md +20 -11
  11. package/agent-assets/reference/app-creation.md +42 -20
  12. package/agent-assets/reference/architecture.md +24 -22
  13. package/agent-assets/reference/local-dev.md +230 -67
  14. package/agent-assets/reference/mcp-integration.md +9 -9
  15. package/agent-assets/reference/platform-api-consumer.md +244 -0
  16. package/agent-assets/reference/workspace-config.md +7 -7
  17. package/agent-assets/rules/descix_mcp.template.mdc +8 -8
  18. package/bin/descix.js +2559 -1435
  19. package/bin/kb-deny-lint.mjs +91 -0
  20. package/bin/mcp-server.js +175 -91
  21. package/lib/agent-files.js +37 -25
  22. package/lib/api-client.js +223 -56
  23. package/lib/auth-guard.js +13 -3
  24. package/lib/chat-session.js +187 -0
  25. package/lib/cli-version.js +59 -0
  26. package/lib/commands/airdrop.js +251 -0
  27. package/lib/commands/auth.js +254 -72
  28. package/lib/commands/briefer/errors.js +88 -0
  29. package/lib/commands/briefer/index.js +319 -0
  30. package/lib/commands/briefer/sources/canonical-sources.js +129 -0
  31. package/lib/commands/briefer/sources/entitlements.js +209 -0
  32. package/lib/commands/briefer/sources/environments.js +205 -0
  33. package/lib/commands/briefer/sources/identifiers.js +214 -0
  34. package/lib/commands/briefer/sources/microservice-deploy.js +267 -0
  35. package/lib/commands/briefer/sources/routing.js +275 -0
  36. package/lib/commands/briefer/sources/what-is-not.js +223 -0
  37. package/lib/commands/briefer/stitcher.js +134 -0
  38. package/lib/commands/briefer/util/source-reader.js +502 -0
  39. package/lib/commands/config.js +210 -90
  40. package/lib/commands/corpus.js +978 -0
  41. package/lib/commands/credits.js +122 -0
  42. package/lib/commands/health.js +627 -0
  43. package/lib/commands/kb-list-render.js +32 -0
  44. package/lib/commands/kb.js +190 -413
  45. package/lib/commands/mcp.js +5 -5
  46. package/lib/commands/microservice-restart.js +359 -0
  47. package/lib/commands/model-config.js +313 -0
  48. package/lib/commands/retired-kb-sync.js +134 -0
  49. package/lib/commands/serve.js +26 -1
  50. package/lib/commands/status.js +23 -51
  51. package/lib/commands/update.js +36 -107
  52. package/lib/core/Chunker.js +491 -17
  53. package/lib/core/CorpusDenyLint.js +326 -0
  54. package/lib/core/CorpusWalker.js +357 -0
  55. package/lib/core/Hydrator.js +75 -29
  56. package/lib/core/ManifestLoader.js +533 -0
  57. package/lib/core/SiteWalker.js +218 -0
  58. package/lib/core/Syncer.js +163 -11
  59. package/lib/diagnostic-log.js +41 -0
  60. package/lib/environment-report.js +100 -0
  61. package/lib/global-config.js +4 -1
  62. package/lib/oauth-client.js +164 -0
  63. package/lib/origin.js +143 -0
  64. package/lib/service-api-client.js +95 -0
  65. package/lib/wallet-environment.js +49 -0
  66. package/lib/wallet-file.js +16 -2
  67. package/lib/wizard/setup.js +10 -9
  68. package/lib/workspace-config.js +380 -235
  69. package/lib/workspace-identity.js +92 -0
  70. package/lib/workspace-utils.js +41 -61
  71. package/package.json +19 -6
  72. package/templates/agent-claude.md +157 -9
  73. package/templates/agent-cline.md +68 -8
  74. package/templates/agent-copilot.md +68 -8
  75. package/templates/agent-cursor.md +68 -8
  76. package/templates/alignment-schema.json +20 -0
  77. package/templates/cloud-brain-hook.sh +20 -0
  78. package/templates/drive/README.md +1 -1
  79. package/templates/drive_templates/README.md +2 -2
  80. package/templates/scaffolds/README.md +6 -10
  81. package/templates/scaffolds/microservice/SERVICE_README_sdk.md +30 -0
  82. package/templates/scaffolds/microservice/app.js +51 -10
  83. package/templates/scaffolds/microservice/app.yaml +2 -2
  84. package/templates/scaffolds/microservice/defaults-config-dev.json +4 -0
  85. package/templates/scaffolds/microservice/dev-overrides.example.json +5 -0
  86. package/templates/scaffolds/microservice/manifest.json +0 -1
  87. package/templates/scaffolds/microservice/package.json +3 -3
  88. package/templates/scaffolds/microservice/scripts/register.js +56 -41
  89. package/templates/scaffolds/microservice/services/mcpClient.js +55 -65
  90. package/templates/scaffolds/microservice/services/utils.js +20 -238
  91. package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +24 -0
  92. package/lib/commands/app-wizard.js +0 -627
  93. package/lib/commands/folder.js +0 -291
  94. package/lib/core/PathContext.js +0 -335
  95. package/templates/scaffolds/microservice/dev-overrides.json.example +0 -5
  96. package/templates/scaffolds/site/DeSciXAppSDK.js +0 -62
  97. package/templates/scaffolds/site/README.md +0 -51
  98. package/templates/scaffolds/site/app.js +0 -22
  99. package/templates/scaffolds/site/index.html +0 -42
  100. package/templates/scaffolds/site/styles.css +0 -123
@@ -1,78 +1,68 @@
1
1
  /**
2
- * DeSciX MCP Loopback Client
3
- *
4
- * Enables the service to call Core Tools (e.g. RAG, Purchase) or other Service Tools
5
- * via the Federated MCP Broker (DeSciX_Cloud).
2
+ * DeSciX service api-client — the CLI's REST api-client running in THIS microservice.
3
+ *
4
+ * AUTH MODEL — the microservice IS the CLI's api-client running in the cloud
5
+ * (CEO-D-2026-06-02-APP-MICROSERVICE-IS-CLI-CLIENT-WALLET-SIG):
6
+ * This service authenticates to /apifront EXACTLY like the CLI: it holds the DEVELOPER'S OWN
7
+ * durable credential (wallet_address + signature, from the developer's .descix/wallet.json),
8
+ * presents it to `reconnect_by_wallet` to mint a session access_token, and calls /apifront AS
9
+ * THE DEVELOPER. There is NO SA-OIDC, NO delegate signature, NO X-NFT-ID, NO SERVICE_KEY, NO
10
+ * Signer. The packaged `@descix/cli` `createServiceApiClient` does all of this.
11
+ *
12
+ * CREDENTIAL PLACEMENT (org rule — no checked-in credentials):
13
+ * The developer copies their wallet `signature` + `wallet_address` into the service config as
14
+ * `DEVELOPER_SIGNATURE` / `DEVELOPER_WALLET_ADDRESS`, in the GITIGNORED dev-overrides.json (dev)
15
+ * / a SECRET (prod) — NEVER the checked-in defaults-config.json (the signature is a credential).
16
+ * We HARD-FAIL (no fallback) if either is absent.
6
17
  */
7
18
 
8
- import axios from 'axios';
19
+ import { createServiceApiClient } from '@descix/cli/service-api-client';
9
20
  import { utils } from './utils.js';
10
- import { Signer } from '@descix/sdk'; // Assuming descix-sdk is available in the container
11
21
 
12
- class McpClient {
13
- constructor() {
14
- // Initialize signer if key is available
15
- if (utils.SERVICE_KEY) {
16
- this.signer = new Signer(utils.SERVICE_KEY.privateKey, utils.SERVICE_KEY.slotId);
17
- }
22
+ let _service = null;
23
+
24
+ /**
25
+ * Build (once) the developer-authed service api-client for this microservice.
26
+ * Reads the developer credential from config and HARD-FAILS if absent (no fallback).
27
+ */
28
+ function getService() {
29
+ if (_service) return _service;
30
+ if (!utils.CORE_API_URL) {
31
+ throw new Error('CORE_API_URL not configured.');
18
32
  }
33
+ if (!utils.DEVELOPER_WALLET_ADDRESS || !utils.DEVELOPER_SIGNATURE) {
34
+ throw new Error(
35
+ 'Developer credential missing: set DEVELOPER_WALLET_ADDRESS + DEVELOPER_SIGNATURE in the ' +
36
+ 'GITIGNORED dev-overrides.json (dev) / a secret (prod) — never defaults-config.json. ' +
37
+ 'These are the developer\'s own wallet_address + signature (from .descix/wallet.json). No fallback.'
38
+ );
39
+ }
40
+ _service = createServiceApiClient({
41
+ baseUrl: utils.CORE_API_URL,
42
+ walletAddress: utils.DEVELOPER_WALLET_ADDRESS,
43
+ signature: utils.DEVELOPER_SIGNATURE,
44
+ });
45
+ return _service;
46
+ }
19
47
 
48
+ class McpClient {
20
49
  /**
21
- * Call a tool via the Core Broker
22
- * @param {string} toolName - Name of the tool to call (e.g., 'query_knowledge_base')
23
- * @param {Object} args - Tool arguments
24
- * @param {Object} context - User context object (from _descix) to pass identity
25
- * @returns {Promise<any>} - Tool result
50
+ * Call a Core command via /apifront, authenticated AS the developer.
51
+ * @param {string} command - Command name (e.g. 'app_records_put', 'get_app_asset')
52
+ * @param {Object} args - Command params
53
+ * @returns {Promise<any>} - Core's `message` payload on success
26
54
  */
27
- async callTool(toolName, args, context) {
28
- if (!utils.CORE_API_URL) {
29
- throw new Error('CORE_API_URL not configured');
30
- }
31
-
32
- // Construct the payload simulating a PWA or internal request
33
- const payload = {
34
- command: toolName,
35
- params: {
36
- ...args,
37
- // Critical: Pass the original user context back to Core
38
- // The Core trusts this service (authenticated via Service Secret or VPC)
39
- // In this simplified template, we pass the user ID we received
40
- user_id: context?.user?.id || utils.OWNER_USER_ID,
41
- // If we have owner credentials, use them as fallback for auth
42
- wallet_address: utils.OWNER_WALLET_ADDRESS,
43
- signature: utils.OWNER_SIGNATURE
44
- }
45
- };
46
-
47
- try {
48
- console.log(`[McpClient] Calling ${toolName} on ${utils.CORE_API_URL}`);
49
-
50
- // Get headers (Signature or Legacy Secret)
51
- const headers = {
52
- 'Content-Type': 'application/json'
53
- };
54
-
55
- if (this.signer) {
56
- // Sign the payload string
57
- const signatureHeaders = this.signer.getHeaders(JSON.stringify(payload));
58
- Object.assign(headers, signatureHeaders);
59
- } else if (utils.SERVICE_SECRET) {
60
- // Fallback to legacy secret
61
- headers['Authorization'] = `Bearer ${utils.SERVICE_SECRET}`;
62
- }
63
-
64
- const response = await axios.post(utils.CORE_API_URL, payload, { headers });
65
-
66
- if (response.data?.status === 'OK') {
67
- return response.data.message;
68
- } else {
69
- throw new Error(response.data?.message || 'Unknown error from Core');
70
- }
71
- } catch (error) {
72
- console.error(`[McpClient] Error calling ${toolName}:`, error.message);
73
- throw error;
74
- }
55
+ async callTool(command, args = {}) {
56
+ return getService().invoke(command, args);
75
57
  }
76
58
  }
77
59
 
78
60
  export const mcpClient = new McpClient();
61
+
62
+ /**
63
+ * The raw `invoke(command, params) => Promise<message>` — the exact shape `fetchAppAsset`
64
+ * (from `@descix/sdk`) and other mesh helpers expect. Prefer this for new code.
65
+ */
66
+ export function getInvoke() {
67
+ return getService().invoke;
68
+ }
@@ -1,247 +1,29 @@
1
1
  /**
2
- * Service Configuration Bootstrap
3
- *
4
- * STANDARD BOOTSTRAP PATTERN:
5
- * - .env: Only bootstrap keys (CONFIG_SECRET_NAME, CONFIG_SECRET_VERSION, DEPLOY_ENV)
6
- * - defaults-config.json: Instance defaults (checked in)
7
- * - Secret Manager: Global config
8
- * - dev-overrides.json: Local dev overrides (gitignored)
2
+ * Service Configuration — Thin wrapper over @descix/cloud-core
3
+ *
4
+ * Uses @descix/cloud-core for shared bootstrap logic:
5
+ * - Layered config: Env -> .env -> workspace.json
6
+ * - Secret Manager integration
7
+ * - Auto-port detection in dev
8
+ *
9
+ * This is the ONLY path to config values. Never use process.env directly.
10
+ * Never add fallback values to utils.CONFIG_VALUE — nulls surface misconfigurations.
9
11
  */
10
12
 
11
13
  import { fileURLToPath } from 'url';
12
- import { GoogleAuth } from 'google-auth-library';
13
- import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
14
- import dotenv from 'dotenv';
15
14
  import path from 'path';
16
- import fs from 'fs';
17
- import { createRequire } from 'module';
15
+ import {
16
+ createCloudConfig,
17
+ getCloudConfig,
18
+ initializeCloudConfig,
19
+ } from '@descix/cloud-core';
18
20
 
19
- const __filename = fileURLToPath(import.meta.url);
20
- const __dirname = path.dirname(__filename);
21
- const require = createRequire(import.meta.url);
21
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
+ const rootPath = path.resolve(__dirname, '..');
22
23
 
23
- class ServiceConfig {
24
- constructor() {
25
- if (ServiceConfig.instance) return ServiceConfig.instance;
24
+ // Initialize CloudConfig with service root
25
+ createCloudConfig({ rootPath });
26
26
 
27
- this.__appDir = path.resolve(__dirname, '../');
27
+ const utils = getCloudConfig();
28
28
 
29
- // 1. Load Bootstrap .env
30
- if (process.env.DEPLOY_ENV !== 'production') {
31
- dotenv.config({ path: path.resolve(this.__appDir, '.env') });
32
- }
33
-
34
- this.CONFIG_SECRET_NAME = process.env.CONFIG_SECRET_NAME || null;
35
- this.CONFIG_SECRET_VERSION = process.env.CONFIG_SECRET_VERSION || null;
36
- this.DEPLOY_ENV = process.env.DEPLOY_ENV || null;
37
-
38
- // 2. Initialize Config Keys (All NULL - no hardcoded defaults)
39
- this.DEBUG_LOCAL = null;
40
- this.LOCAL_PORT = null;
41
- this.CORE_API_URL = null; // URL for DeSciX Core (for loopback)
42
- this.SERVICE_SECRET = null; // Secret for authenticating loopback calls (Legacy)
43
- this.SERVICE_KEY = null; // Service Delegate Key (New)
44
-
45
- // Owner Credentials (for loopback API calls)
46
- this.OWNER_WALLET_ADDRESS = null;
47
- this.OWNER_SIGNATURE = null;
48
- this.OWNER_USER_ID = null;
49
-
50
- this.GOOGLE_PROJECT_ID = null;
51
- this.secretClient = null;
52
- this.PORT = process.env.PORT || null;
53
-
54
- // Load instance defaults immediately
55
- this._loadDefaults();
56
-
57
- ServiceConfig.instance = this;
58
- }
59
-
60
- _mergeConfig(config) {
61
- for (const [key, value] of Object.entries(config)) {
62
- if (key.startsWith('_')) continue;
63
- // Allow dynamic expansion (undefined) or overwrite (null/value)
64
- this[key] = value;
65
- }
66
- }
67
-
68
- _loadDefaults() {
69
- const defaultsPath = path.resolve(this.__appDir, 'defaults-config.json');
70
- if (fs.existsSync(defaultsPath)) {
71
- try {
72
- this._mergeConfig(require(defaultsPath));
73
- console.log('[Config] Loaded defaults-config.json');
74
- } catch (e) {
75
- console.error('[Config] Error loading defaults-config.json:', e.message);
76
- }
77
- }
78
- }
79
-
80
- _loadDevOverrides() {
81
- if (this.DEPLOY_ENV !== 'dev') return;
82
- const overridesPath = path.resolve(this.__appDir, 'dev-overrides.json');
83
- if (fs.existsSync(overridesPath)) {
84
- try {
85
- this._mergeConfig(require(overridesPath));
86
- console.log('[Config] Loaded dev-overrides.json');
87
- } catch (e) {
88
- console.error('[Config] Error loading dev-overrides.json:', e.message);
89
- }
90
- }
91
- }
92
-
93
- async accessSecretVersion(secretName, version) {
94
- if (!version) throw new Error('[Config] CRITICAL: CONFIG_SECRET_VERSION not set');
95
- if (!this.secretClient) this.secretClient = new SecretManagerServiceClient();
96
-
97
- const name = `projects/${this.GOOGLE_PROJECT_ID}/secrets/${secretName}/versions/${version}`;
98
- const [response] = await this.secretClient.accessSecretVersion({ name });
99
- return response.payload.data.toString('utf8');
100
- }
101
-
102
- async _registerWithWorkspace() {
103
- if (this.DEPLOY_ENV !== 'dev' && !this.DEBUG_LOCAL) return;
104
-
105
- if (this.DEBUG_LOCAL) {
106
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
107
- }
108
-
109
- let communityId = null;
110
- let appId = null;
111
- let workspacePath = null;
112
-
113
- // Walk up from the service directory to find .descix/workspace.json
114
- let searchDir = this.__appDir;
115
- for (let i = 0; i < 10; i++) {
116
- const candidate = path.resolve(searchDir, '.descix/workspace.json');
117
- if (fs.existsSync(candidate)) {
118
- workspacePath = candidate;
119
- break;
120
- }
121
- const parent = path.dirname(searchDir);
122
- if (parent === searchDir) break;
123
- searchDir = parent;
124
- }
125
-
126
- // Detect context from manifest.json
127
- try {
128
- const manifestPath = path.resolve(this.__appDir, 'manifest.json');
129
- if (fs.existsSync(manifestPath)) {
130
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
131
- communityId = manifest.service?.community_id || manifest.community_id;
132
- appId = manifest.service?.app_id || manifest.app_id;
133
- }
134
- } catch (e) {
135
- console.warn('[Config] Failed to detect app context from manifest:', e.message);
136
- }
137
-
138
- if (!communityId || !appId) {
139
- console.log('[Config] No communityId/appId — skipping workspace registration');
140
- return;
141
- }
142
-
143
- if (!workspacePath) {
144
- console.log('[Config] No .descix/workspace.json found — skipping workspace registration');
145
- return;
146
- }
147
-
148
- // Write service port into workspace.json for descix-serve to pick up
149
- try {
150
- const workspace = JSON.parse(fs.readFileSync(workspacePath, 'utf-8'));
151
- if (!workspace.communities) workspace.communities = {};
152
- if (!workspace.communities[communityId]) workspace.communities[communityId] = { apps: {} };
153
- if (!workspace.communities[communityId].apps) workspace.communities[communityId].apps = {};
154
- if (!workspace.communities[communityId].apps[appId]) workspace.communities[communityId].apps[appId] = {};
155
-
156
- const appEntry = workspace.communities[communityId].apps[appId];
157
- if (!appEntry.service) appEntry.service = {};
158
- appEntry.service.port = parseInt(this.PORT, 10);
159
-
160
- fs.writeFileSync(workspacePath, JSON.stringify(workspace, null, 2), 'utf-8');
161
- console.log(`[Config] Registered ${communityId}/${appId} service port ${this.PORT} in workspace.json`);
162
-
163
- this._workspacePath = workspacePath;
164
- this._registeredCommunityId = communityId;
165
- this._registeredAppId = appId;
166
- } catch (e) {
167
- console.warn('[Config] Failed to write workspace.json:', e.message);
168
- }
169
- }
170
-
171
- cleanupWorkspaceRegistration() {
172
- if (!this._workspacePath || !this._registeredCommunityId || !this._registeredAppId) return;
173
- try {
174
- const workspace = JSON.parse(fs.readFileSync(this._workspacePath, 'utf-8'));
175
- const appEntry = workspace.communities?.[this._registeredCommunityId]?.apps?.[this._registeredAppId];
176
- if (appEntry?.service) {
177
- delete appEntry.service.port;
178
- }
179
- fs.writeFileSync(this._workspacePath, JSON.stringify(workspace, null, 2), 'utf-8');
180
- console.log(`[Config] Cleaned up workspace registration for ${this._registeredCommunityId}/${this._registeredAppId}`);
181
- } catch {
182
- // Best-effort cleanup on shutdown
183
- }
184
- }
185
-
186
- async initialize() {
187
- // 1. Get Project ID
188
- const auth = new GoogleAuth();
189
- this.GOOGLE_PROJECT_ID = await auth.getProjectId();
190
-
191
- // 2. Load from Secret Manager
192
- if (this.CONFIG_SECRET_NAME) {
193
- try {
194
- const payload = await this.accessSecretVersion(
195
- this.CONFIG_SECRET_NAME,
196
- this.CONFIG_SECRET_VERSION
197
- );
198
- this._mergeConfig(JSON.parse(payload));
199
- console.log(`[Config] Loaded from Secret Manager: ${this.CONFIG_SECRET_NAME}`);
200
- } catch (error) {
201
- console.warn(`[Config] Secret Manager unavailable:`, error.message);
202
- }
203
- }
204
-
205
- // 3. Apply Dev Overrides
206
- this._loadDevOverrides();
207
-
208
- // 4. Validate Port (Allow 'auto' or 0)
209
- if (!this.PORT && this.PORT !== 0) {
210
- if (this.DEBUG_LOCAL && this.LOCAL_PORT) {
211
- this.PORT = this.LOCAL_PORT;
212
- } else {
213
- // If dev, default to auto-assign
214
- if (this.DEPLOY_ENV === 'dev' || this.DEBUG_LOCAL) {
215
- this.PORT = 'auto';
216
- } else {
217
- throw new Error('[Config] CRITICAL: PORT not configured');
218
- }
219
- }
220
- }
221
-
222
- // 5. Default Core API URL if not set
223
- if (!this.CORE_API_URL) {
224
- this.CORE_API_URL = this.DEBUG_LOCAL
225
- ? 'https://localhost:4000/apifront'
226
- : 'https://descix-cloud.descix.net/apifront';
227
- }
228
-
229
- // 6. Register service port in workspace.json for descix-serve gateway
230
- await this._registerWithWorkspace();
231
-
232
- // Clean up registration on shutdown
233
- process.on('SIGINT', () => this.cleanupWorkspaceRegistration());
234
- process.on('SIGTERM', () => this.cleanupWorkspaceRegistration());
235
-
236
- console.log(`[Config] Final: port=${this.PORT}, core=${this.CORE_API_URL}`);
237
- }
238
- }
239
-
240
- const serviceConfig = new ServiceConfig();
241
-
242
- async function initializeServiceConfig() {
243
- await serviceConfig.initialize();
244
- return serviceConfig;
245
- }
246
-
247
- export { initializeServiceConfig, serviceConfig as utils };
29
+ export { initializeCloudConfig as initializeServiceConfig, utils };
@@ -6,6 +6,30 @@
6
6
  Include the key use cases, benefits, and target users.
7
7
  This section is critical for tell_me_how discovery - be descriptive about WHEN to use this service.}
8
8
 
9
+ ## Onboarding — Canonical Mesh Microservice Setup
10
+
11
+ Run this sequence ONCE to stand up a microservice that can call the mesh
12
+ (Core Tools like `query_knowledge_base`, or other services' tools) over the
13
+ `/apifront` broker:
14
+
15
+ ```bash
16
+ descix app create -c {community} -a {app} # create the app (or hydrate an existing one)
17
+ descix app init -a {community}-{app} # scaffold site/ kb/ microservice/
18
+ descix app set-port -a {community}-{app} -p {port} # assign the microservice's loopback port in workspace.json
19
+ descix microservice register # register the service manifest with the Core gateway
20
+ # then: cp dev-overrides.example.json dev-overrides.json and fill in the developer credential
21
+ ```
22
+
23
+ > **Calling /apifront — the microservice IS the CLI's api-client in the cloud**
24
+ > (CEO-D-2026-06-02-APP-MICROSERVICE-IS-CLI-CLIENT-WALLET-SIG). The scaffold
25
+ > `mcpClient` authenticates AS the developer: it reads `DEVELOPER_WALLET_ADDRESS`
26
+ > + `DEVELOPER_SIGNATURE` (the developer's own wallet credential, from
27
+ > `.descix/wallet.json`) and calls `reconnect_by_wallet` to mint a session —
28
+ > exactly like the CLI. Put the credential in the GITIGNORED `dev-overrides.json`
29
+ > (dev) / a secret (prod), NEVER `defaults-config.json`. There is NO
30
+ > `register-delegate`, NO `SERVICE_KEY`, NO service account for the data plane.
31
+ > **HTTP 401?** The developer credential is missing or wrong — check those two keys.
32
+
9
33
  ## Prerequisites
10
34
 
11
35
  - {Any setup requirements}