Keys test
Example apps / keys-test / v0.1.0
Node modkeys:rednode-test permissionneeds rednode
Reads one of the player's shared keys with secrets.shared, asks for it with secrets.askShared, and moves a key the package kept itself into the shared store.
A preview has no keys: every read gives null, and the ask only logs what the game would show.
// Keys test: REDnode 0.48.0's shared keys for packages (#23, K3), with a made-up service,
// "rednode-test", so the player's real keys are never touched.
//
// 1. secrets.shared reads a shared key: null until the player allows this package
// ("keys:rednode-test" in the manifest) or while no key is set.
// 2. secrets.askShared asks the player in REDnode's own Key request window: allow, and paste
// a key when none is saved. The answer is the key, or null.
// 3. The offer: a key this package kept in its own secrets moves to the shared store, as
// REDagent's and EDGEwave Voice's do.
// 4. A key the manifest does not list is refused.
// Only a key's last 4 characters are ever shown or logged.
import { secrets, ui } from 'rednode';
const SERVICE = 'rednode-test';
const OWN = 'old-test-key';
const status = ui.text('Ready', { tone: 'dim' });
const log = ui.list({ keep: 100, height: 220 });
function say(text, tone) {
log.push(`${new Date().toLocaleTimeString()} ${text}`);
console.log(text);
status.set({ text, tone: tone ?? 'dim' });
}
const shown = (key) => (key ? `a key ending in ${key.slice(-4)}` : 'null');
async function run(label, fn) {
try {
say(`${label}: ${await fn()}`);
} catch (err) {
say(`${label} failed: ${err.message}`, 'danger');
}
}
ui.window({
title: 'Keys test',
anchor: 'right',
x: 40,
y: 80,
width: 500,
height: 560,
closable: true,
children: [
ui.heading('1. Read'),
ui.row(ui.button('secrets.shared', () => run('shared', async () => shown(await secrets.shared(SERVICE))))),
ui.heading('2. Ask'),
ui.row(
ui.button('secrets.askShared', () => run('askShared', async () => shown(await secrets.askShared(SERVICE, { reason: 'For the Keys test' })))),
),
ui.heading('3. Offer its own key'),
ui.row(
ui.button('Keep an own key', () => run('own key', async () => (await secrets.set(OWN, 'rnt-own-key-7k3q'), 'kept one ending in 7k3q'))),
ui.button('Ask, offering it', async () => {
await run('askShared with offer', async () => {
const mine = await secrets.get(OWN);
const key = await secrets.askShared(SERVICE, { reason: 'Move the Keys test key', offer: mine ?? undefined });
if (key && mine) await secrets.delete(OWN);
return `${shown(key)}${key && mine ? '; own copy deleted' : ''}`;
});
}, { variant: 'ghost' }),
),
ui.heading('4. Not listed'),
ui.row(ui.button('secrets.shared("openai")', () => run('shared("openai")', async () => shown(await secrets.shared('openai'))), { variant: 'ghost' })),
status,
log,
],
});
say('Ready. Uses the made-up service "rednode-test"; remove it in Keys after the test.');
REDnode runs JavaScript. The rednode package also has TypeScript types, so an editor checks these calls; compile TypeScript to .mjs first.
{
"id": "keys-test",
"name": "Keys test",
"version": "0.1.0",
"author": "Edgemodders",
"description": "Checks REDnode 0.48.0's shared keys for packages (#23): secrets.shared, secrets.askShared with REDnode's Key request window, a package's own key moved to the shared store, and a key it does not list refused. Uses a made-up service, rednode-test.",
"main": "mod.mjs",
"permissions": ["keys:rednode-test"],
"requires": { "rednode": ">=0.48.0" },
"open": false
}
The manifest names the package and says how REDnode runs it. Its fields are in Overlay apps.
How it works
The manifest lists "keys:rednode-test", a made-up service, so the test never touches a real key. secrets.shared answers null until the player allows the package or while no key is set.
secrets.askShared opens REDnode's own Key request window: the player allows the package and, when no key is saved, pastes one. The package gets the key, or null when the player denies.
With offer, a key the package kept in its own secrets moves to the shared store; the window shows only its last 4 characters. Then the package deletes its own copy, as REDagent does.
A key the manifest does not list is refused with an error.
What it uses
secrets.shared secrets.askShared keys:<service> permission
Try this
- Press secrets.shared, then secrets.askShared.
- Press Keep an own key, then Ask, offering it.
- Press secrets.shared("openai").
Use it
Copy the files into red4ext/plugins/REDnode/apps/keys-test/ in the game folder, or start your own package from them: change id and name in the manifest first. The whole folder is on GitHub.