Skip to content

Commit 0e9401e

Browse files
committed
rename specs to cumulusvpn<cc> and reprice premium to 20 FLUX/month
Spec/app naming: cumulus<cc> -> cumulusvpn<cc> (e.g. cumulusde -> cumulusvpnde) so the Flux app name carries the brand. Updated the generator, templates, register/scale/encrypt/validate script docs, the deploy test, and every client that derives a country from the spec name (web specToCountryCode now strips the `cumulusvpn` prefix; core-ts already takes country from the Flux location API). Price: premium 4.5 -> 20 FLUX/month (still ~$0.99). Updated the gateway env in the generator + plain template, the signed directory (re-signed), all three client bundles, the public web directory, the landing page, and the docs. Directory: re-signed directory.signed.json with the new spec names + price 20 using the canonical key (1e+42…). Unified all clients on that one key — mobile previously pinned a different key (IhvN6…) and bundled a placeholder payment address; web/mobile bundles + web/public now carry the identical signed directory, and mobile's pinned CVPN_DIRECTORY_PUBKEY matches. Left as-is: gateway internal *_test.go use 4.5 as an arbitrary price constant for entitlement/parsing logic (self-consistent, not the real price). The generated brand/marketing PNGs still render the old price — regenerate via brand/generated/marketing/_build-marketing.py before store submission. Verified: deploy 8/8, core-ts 46/46 (+build), web 24/24, desktop 11/11, mobile 11/11, all typecheck+lint+format clean, directory signature valid.
1 parent 1c99d9a commit 0e9401e

34 files changed

Lines changed: 130 additions & 117 deletions

brand/generated/marketing/_build-marketing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def screen_payment():
208208
return '''<div class="pay">
209209
<h3>Upgrade to full speed</h3>
210210
<div class="sub">Send FLUX with the exact message below. Every gateway unlocks your key within ~1 minute — no account needed.</div>
211-
<div class="amount"><div class="big">4.5 FLUX</div><div class="usd">≈ $0.99 · 30 days</div></div>
211+
<div class="amount"><div class="big">20 FLUX</div><div class="usd">≈ $0.99 · 30 days</div></div>
212212
<div class="qr"></div>
213213
<div class="field"><div class="lab">Pay to address</div><div class="val"><span>t1cUmuLus…9xVQ2f</span><span class="cp">Copy</span></div></div>
214214
<div class="field"><div class="lab">Message (required)</div><div class="val"><span>CVPN1:3QJmnh8vzBqoQpuTGD</span><span class="cp">Copy</span></div></div>

clients/core-ts/src/discovery.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ function info(overrides: Partial<InfoResponse>): InfoResponse {
2525
describe('discoverGateways', () => {
2626
it('queries Flux + nodes, probes /v1/info, and returns verified gateways sorted by country then load', async () => {
2727
const routes: Record<string, Response> = {
28-
'https://api.runonflux.io/apps/location/cumulusde': jsonResponse({
28+
'https://api.runonflux.io/apps/location/cumulusvpnde': jsonResponse({
2929
status: 'success',
3030
data: [{ ip: '1.2.3.4:16127' }, { ip: '5.6.7.8' }],
3131
}),
32-
'http://9.9.9.9:16127/apps/location/cumulusde': jsonResponse({
32+
'http://9.9.9.9:16127/apps/location/cumulusvpnde': jsonResponse({
3333
status: 'success',
3434
data: [{ ip: '10.0.0.1' }],
3535
}),
@@ -48,7 +48,7 @@ describe('discoverGateways', () => {
4848
return res;
4949
};
5050

51-
const gateways = await discoverGateways(['cumulusde'], { nodes: ['9.9.9.9'], fetchImpl });
51+
const gateways = await discoverGateways(['cumulusvpnde'], { nodes: ['9.9.9.9'], fetchImpl });
5252

5353
expect(gateways.map((g) => `${g.country}:${g.ip}`)).toEqual([
5454
'AT:5.6.7.8', // country asc
@@ -58,21 +58,21 @@ describe('discoverGateways', () => {
5858
// port was stripped from the api.runonflux entry
5959
expect(calls).toContain('http://1.2.3.4:51821/v1/info');
6060
// node index was queried directly
61-
expect(calls).toContain('http://9.9.9.9:16127/apps/location/cumulusde');
61+
expect(calls).toContain('http://9.9.9.9:16127/apps/location/cumulusvpnde');
6262
});
6363

6464
it('drops unreachable candidates', async () => {
6565
const fetchImpl: FetchImpl = async (input) => {
6666
const url = String(input);
67-
if (url.endsWith('/apps/location/cumulusus')) {
67+
if (url.endsWith('/apps/location/cumulusvpnus')) {
6868
return jsonResponse({ status: 'success', data: [{ ip: '1.1.1.1' }, { ip: '2.2.2.2' }] });
6969
}
7070
if (url === 'http://1.1.1.1:51821/v1/info') {
7171
return signedResponse(info({ country: 'US' }), signer);
7272
}
7373
throw new Error('connection refused');
7474
};
75-
const gateways = await discoverGateways(['cumulusus'], { fetchImpl });
75+
const gateways = await discoverGateways(['cumulusvpnus'], { fetchImpl });
7676
expect(gateways).toHaveLength(1);
7777
expect(gateways[0]!.ip).toBe('1.1.1.1');
7878
});
@@ -81,7 +81,7 @@ describe('discoverGateways', () => {
8181
const otherSigner = makeSignKeypair();
8282
const fetchImpl: FetchImpl = async (input) => {
8383
const url = String(input);
84-
if (url.endsWith('/apps/location/cumulusde')) {
84+
if (url.endsWith('/apps/location/cumulusvpnde')) {
8585
return jsonResponse({ status: 'success', data: [{ ip: '3.3.3.3' }] });
8686
}
8787
// Body is signed by otherSigner but advertises signer's pubkey → invalid.
@@ -94,7 +94,7 @@ describe('discoverGateways', () => {
9494
},
9595
});
9696
};
97-
const gateways = await discoverGateways(['cumulusde'], { fetchImpl });
97+
const gateways = await discoverGateways(['cumulusvpnde'], { fetchImpl });
9898
expect(gateways).toHaveLength(0);
9999
});
100100
});
@@ -125,8 +125,8 @@ describe('directoryVerify', () => {
125125
version: 1,
126126
updated: '2026-07-16T00:00:00Z',
127127
payment_address: 't1abc',
128-
price_flux: 4.5,
129-
specs: ['cumulusde', 'cumulusus'],
128+
price_flux: 20,
129+
specs: ['cumulusvpnde', 'cumulusvpnus'],
130130
seed_gateways: [{ ip: '1.2.3.4', country: 'DE', sign_pubkey: 'x' }],
131131
};
132132

clients/core-ts/src/discovery.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ async function probe(ip: string, fetchImpl: FetchImpl): Promise<GatewayInfo | nu
6464
* country and sorted by load (ascending) so the caller can pick the least-busy
6565
* gateway in a given country.
6666
*
67-
* @param specNames - Flux app-spec names, e.g. `["cumulusde", "cumulusus"]`.
67+
* @param specNames - Flux app-spec names, e.g. `["cumulusvpnde", "cumulusvpnus"]`.
6868
* @param options - Optional extra Flux nodes and a custom fetch implementation.
6969
* @returns Verified gateways, grouped by country and sorted by load.
7070
*/

clients/core-ts/src/enroll.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const enrollData: EnrollResponse = {
1515
dns: '1.1.1.1',
1616
payment_address: 't1abc',
1717
payment_memo: 'CVPN1:2RkUfDC55GMndKreXqK7Jruu8Snx',
18-
price_flux: 4.5,
18+
price_flux: 20,
1919
};
2020

2121
describe('enroll', () => {

clients/core-ts/src/paymentCode.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ describe('paymentMemo', () => {
3737
describe('walletDeepLink', () => {
3838
it('builds the flux: URI exactly per contract', () => {
3939
const memo = paymentMemo(ZERO_PUB);
40-
expect(walletDeepLink('t1abc', 4.5, memo)).toBe(
41-
`flux:t1abc?amount=4.5&message=CVPN1:${ZERO_CODE}`,
40+
expect(walletDeepLink('t1abc', 20, memo)).toBe(
41+
`flux:t1abc?amount=20&message=CVPN1:${ZERO_CODE}`,
4242
);
4343
});
4444
});

clients/desktop/src/lib/directory.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('BUNDLED_DIRECTORY', () => {
2828
});
2929

3030
it('lists a spec per fleet country', () => {
31-
expect(BUNDLED_DIRECTORY.specs).toContain('cumulusde');
31+
expect(BUNDLED_DIRECTORY.specs).toContain('cumulusvpnde');
3232
expect(BUNDLED_DIRECTORY.specs.every((s) => s.startsWith('cumulus'))).toBe(true);
3333
});
3434
});

clients/desktop/src/lib/directory.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,21 @@ export const BUNDLED_DIRECTORY: Directory = {
2020
version: 1,
2121
updated: '2026-07-01T00:00:00Z',
2222
payment_address: 't1PLACEHOLDERpaymentAddressREPLACEatRelease0',
23-
price_flux: 4.5,
24-
specs: ['cumulusde', 'cumulusus', 'cumulusnl', 'cumulussg', 'cumulusuk'],
23+
price_flux: 20,
24+
specs: [
25+
'cumulusvpnau',
26+
'cumulusvpnbr',
27+
'cumulusvpnca',
28+
'cumulusvpncz',
29+
'cumulusvpnde',
30+
'cumulusvpnfr',
31+
'cumulusvpngb',
32+
'cumulusvpnjp',
33+
'cumulusvpnnl',
34+
'cumulusvpnpl',
35+
'cumulusvpnsg',
36+
'cumulusvpnus',
37+
],
2538
seed_gateways: [
2639
{ ip: '203.0.113.10', country: 'DE', sign_pubkey: '' },
2740
{ ip: '203.0.113.20', country: 'US', sign_pubkey: '' },

clients/desktop/src/lib/session.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('establish', () => {
9898
dns: '10.8.0.1',
9999
payment_address: 't1addr',
100100
payment_memo: 'CVPN1:code',
101-
price_flux: 4.5,
101+
price_flux: 20,
102102
};
103103

104104
it('enrolls the key, pins the gateway sign key, and brings the tunnel up', async () => {
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
{
22
"version": 1,
3-
"updated": "2026-07-16T10:59:43Z",
4-
"payment_address": "t1exampleADDRESSxxxxxxxxxxxxxxxxxx",
5-
"price_flux": 4.5,
3+
"updated": "2026-07-17T14:12:07Z",
4+
"payment_address": "t3disq3aZz8K3RLZL9zfkpP2UWNVV3hq4vZ",
5+
"price_flux": 20,
66
"specs": [
7-
"cumulusau",
8-
"cumulusbr",
9-
"cumulusca",
10-
"cumuluscz",
11-
"cumulusde",
12-
"cumulusfr",
13-
"cumulusgb",
14-
"cumulusjp",
15-
"cumulusnl",
16-
"cumuluspl",
17-
"cumulussg",
18-
"cumulusus"
7+
"cumulusvpnau",
8+
"cumulusvpnbr",
9+
"cumulusvpnca",
10+
"cumulusvpncz",
11+
"cumulusvpnde",
12+
"cumulusvpnfr",
13+
"cumulusvpngb",
14+
"cumulusvpnjp",
15+
"cumulusvpnnl",
16+
"cumulusvpnpl",
17+
"cumulusvpnsg",
18+
"cumulusvpnus"
1919
],
2020
"seed_gateways": [
2121
{
@@ -24,6 +24,6 @@
2424
"sign_pubkey": "REPLACE_GATEWAY_ED25519_PUBKEY_B64"
2525
}
2626
],
27-
"sign_pubkey": "IhvN6OfPIjw1y+I+zK3Z/63gsgL1qEt7f8M9UZqRzIU=",
28-
"sig": "6hw21bXnG05i2Un5lvFd47GbV6XlMVTd8nnqSGfZguUd+P/nTXG0VmDa+j21ICgkesC8zo6s0f3RjqHvBqIKAg=="
27+
"sign_pubkey": "1e+42nEpmdjf/cAHs+yE2E2iwmAADpWiLy1VMepsKKw=",
28+
"sig": "TsQTer0bKqMjb0mUZGKwwy5Lr0bDVaUKXynxZRoCJKk3Ps7D4Q/AREEgAZWuTT6Ob7SZQmRpf8ARwo4rK9LHAA=="
2929
}

clients/mobile/src/lib/directory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import bundled from '../data/directory.json';
1818
* Pinned directory Ed25519 public key (`CVPN_DIRECTORY_PUBKEY`), base64.
1919
* Baked into the binary; the whole trust chain hangs off this constant.
2020
*/
21-
export const CVPN_DIRECTORY_PUBKEY = 'IhvN6OfPIjw1y+I+zK3Z/63gsgL1qEt7f8M9UZqRzIU=';
21+
export const CVPN_DIRECTORY_PUBKEY = '1e+42nEpmdjf/cAHs+yE2E2iwmAADpWiLy1VMepsKKw=';
2222

2323
/** The bundled snapshot, typed as the core `Directory`. */
2424
export const bundledDirectory: Directory = bundled as Directory;

0 commit comments

Comments
 (0)