-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathconnection-credential-store.ts
More file actions
79 lines (70 loc) · 3.15 KB
/
Copy pathconnection-credential-store.ts
File metadata and controls
79 lines (70 loc) · 3.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
/**
* ConnectionCredentialStore — where a SELF-HOSTED runtime keeps the
* credential it received at bind time (cloud ADR-0008 consumption side).
*
* Cloud-hosted runtimes authenticate to the control plane with an
* env→cloud service key (`OS_CLOUD_API_KEY`, injected by the cloud).
* Self-hosted runtimes have no such key: their identity ceremony is the
* RFC 8628 device-code bind, whose response carries a one-time
* `runtime_token` (`oscc_…`). This store persists that bearer — plus the
* environment id the binding established — under the runtime's own
* working directory, next to the LocalManifestSource ledger:
*
* <cwd>/.objectstack/cloud-connection.json
*
* Like everything on the runtime's serving path, reads are local file
* operations: presenting the credential is how the runtime reaches the
* control plane for org-scoped catalog/install calls, but nothing at
* boot or serve time DEPENDS on those calls succeeding.
*
* Treat the file as a secret (it is written 0600).
*/
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
/** Persisted binding credential + context. */
export interface StoredConnectionCredential {
/** The `oscc_…` runtime bearer returned ONCE by the bind route. */
runtimeToken: string;
/** Control-plane environment id this runtime is bound as. */
environmentId: string;
/** Control-plane base URL the binding was made against. */
controlPlaneUrl?: string;
organizationId?: string;
accountEmail?: string;
boundAt?: string;
}
/** Default store location, relative to the runtime's working directory. */
export const DEFAULT_CONNECTION_CREDENTIAL_PATH = '.objectstack/cloud-connection.json';
export class ConnectionCredentialStore {
/** Resolved file path. */
readonly path: string;
constructor(path?: string) {
this.path = path
? resolve(path)
: resolve(process.cwd(), DEFAULT_CONNECTION_CREDENTIAL_PATH);
}
/** Read the stored credential; null when absent or unreadable. */
read(): StoredConnectionCredential | null {
if (!existsSync(this.path)) return null;
try {
const parsed = JSON.parse(readFileSync(this.path, 'utf8'));
if (!parsed || typeof parsed.runtimeToken !== 'string' || !parsed.runtimeToken) return null;
if (typeof parsed.environmentId !== 'string' || !parsed.environmentId) return null;
return parsed as StoredConnectionCredential;
} catch {
return null;
}
}
/** Persist (replace) the credential. Written 0600 — it is a secret. */
write(credential: StoredConnectionCredential): void {
mkdirSync(dirname(this.path), { recursive: true });
writeFileSync(this.path, JSON.stringify(credential, null, 2), { encoding: 'utf8', mode: 0o600 });
}
/** Remove the credential (unbind). Returns false when nothing was stored. */
clear(): boolean {
if (!existsSync(this.path)) return false;
unlinkSync(this.path);
return true;
}
}