mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-19 16:27:37 +00:00
Align Relay copy with standalone tier
This commit is contained in:
parent
274bb4c60f
commit
7a48cd4afd
12 changed files with 73 additions and 15 deletions
|
|
@ -554,14 +554,14 @@ curl -s http://localhost:7655/api/monitoring/scheduler/health | jq
|
|||
|
||||
Use the API endpoint above or export diagnostics from **Settings → Diagnostics** when troubleshooting.
|
||||
|
||||
### Relay Security (Pro)
|
||||
### Relay Security (Relay and Above)
|
||||
|
||||
The relay protocol provides mobile remote access with end-to-end encryption:
|
||||
|
||||
- **ECDH key exchange**: Per-channel encryption keys are derived via Elliptic Curve Diffie-Hellman, meaning the relay server never sees plaintext data.
|
||||
- **Per-channel authentication**: Each mobile session authenticates independently.
|
||||
- **Back-pressure**: Data limiters prevent channel flooding.
|
||||
- **License-gated**: Relay functionality requires a Pro or Cloud license.
|
||||
- **License-gated**: Relay functionality requires a Relay, Pro, legacy Pro+, or Cloud license.
|
||||
- **Configurable**: Enable/disable via **Settings → Relay** (admin only).
|
||||
|
||||
### Agent Command Security
|
||||
|
|
|
|||
|
|
@ -32,4 +32,4 @@
|
|||
|
||||
## Mobile Responsive Design
|
||||

|
||||
*Fully responsive mobile interface with a dedicated bottom tab bar for touch navigation. Supports mobile remote access via the relay protocol (Pro feature) for secure monitoring on the go. Touch-optimized controls, adaptive layouts, and the command palette ensure full functionality on smartphones and tablets.*
|
||||
*Fully responsive mobile interface with a dedicated bottom tab bar for touch navigation. Supports mobile remote access through Pulse Relay for secure monitoring on the go. Touch-optimized controls, adaptive layouts, and the command palette ensure full functionality on smartphones and tablets.*
|
||||
|
|
|
|||
|
|
@ -276,6 +276,9 @@ runtime gating as separate unlinked claims.
|
|||
into an operator-actionable activation-required state. Customer-facing
|
||||
Relay settings must not render raw `register:` or license-token-provider
|
||||
diagnostics from the relay client as the primary status message.
|
||||
Relay settings and public commercial docs must also keep Remote Access
|
||||
positioned as a Relay-and-higher capability, not a Pro-only feature, so the
|
||||
Relay tier remains a tangible standalone paid product.
|
||||
19. Add or change cloud plan presentation through `frontend-modern/src/pages/CloudPricing.tsx`
|
||||
That same presentation boundary also owns truthful customer-entry copy for
|
||||
hosted Cloud pricing and signup. Cloud CTA labels, setup steps, and
|
||||
|
|
@ -1697,6 +1700,9 @@ The paid relay settings surface is part of that same ownership model. Changes
|
|||
to `frontend-modern/src/components/Settings/RelaySettingsPanel.tsx` must carry
|
||||
this contract and the dedicated relay frontend proof files instead of
|
||||
remaining unowned consumers of relay licensing state.
|
||||
Its paywall and activation copy must say Relay and higher plans rather than
|
||||
Pro-only wording; Pro may include Relay, but Relay is still its own public
|
||||
self-hosted tier.
|
||||
That relay settings owner is intentionally split by role:
|
||||
`frontend-modern/src/components/Settings/RelaySettingsPanel.tsx` is the
|
||||
settings shell, `frontend-modern/src/components/Settings/useRelaySettingsPanelState.ts`
|
||||
|
|
|
|||
|
|
@ -855,7 +855,9 @@ work extends shared components instead of creating new local variants.
|
|||
`frontend-modern/src/utils/relayPresentation.ts`. The route metadata in
|
||||
`settingsHeaderMeta.ts` and the leading `SettingsPanel` in
|
||||
`RelaySettingsPanel.tsx` must reuse the same description and availability
|
||||
copy instead of drifting into separate rollout or pairing wording.
|
||||
copy instead of drifting into separate rollout or pairing wording. Relay
|
||||
availability copy must describe the Relay tier boundary as Relay and higher
|
||||
plans rather than collapsing Remote Access back into a Pro-only feature.
|
||||
25. Keep shared settings-shell legal and docs referrals on
|
||||
`frontend-modern/src/utils/docsLinks.ts`. Shared settings surfaces such as
|
||||
`AIRuntimeControlsSection.tsx` must not hardcode GitHub `main` doc URLs for
|
||||
|
|
@ -2640,7 +2642,8 @@ The same shell boundary now also owns shared relay route framing copy:
|
|||
the top-level relay settings description and availability copy used by both
|
||||
`settingsHeaderMeta.ts` and `RelaySettingsPanel.tsx`, so the route shell and
|
||||
its first `SettingsPanel` cannot drift into separate rollout or pairing
|
||||
descriptions.
|
||||
descriptions or describe Relay as a Pro-only feature after Relay became its
|
||||
own self-hosted paid tier.
|
||||
|
||||
Single-surface settings pages that only render one canonical `SettingsPanel`
|
||||
must stay rooted directly at that panel instead of wrapping it in an extra
|
||||
|
|
|
|||
|
|
@ -292,7 +292,9 @@ silently inheriting whatever older protocol floor the host runtime would allow.
|
|||
That same rule also applies inside shipped security guidance itself:
|
||||
`SECURITY.md` and the synced `frontend-modern/public/docs/SECURITY.md` copy may
|
||||
not bounce the operator back to GitHub `main` for section references that the
|
||||
running build already owns locally.
|
||||
running build already owns locally. Their Relay security section must also use
|
||||
the current Relay-and-higher entitlement boundary instead of stale Pro-only
|
||||
license wording.
|
||||
That same governed settings trust boundary now also includes
|
||||
`frontend-modern/src/components/Settings/SecurityOverviewPanel.tsx`,
|
||||
`frontend-modern/src/components/Settings/QuickSecuritySetup.tsx`,
|
||||
|
|
|
|||
|
|
@ -554,14 +554,14 @@ curl -s http://localhost:7655/api/monitoring/scheduler/health | jq
|
|||
|
||||
Use the API endpoint above or export diagnostics from **Settings → Diagnostics** when troubleshooting.
|
||||
|
||||
### Relay Security (Pro)
|
||||
### Relay Security (Relay and Above)
|
||||
|
||||
The relay protocol provides mobile remote access with end-to-end encryption:
|
||||
|
||||
- **ECDH key exchange**: Per-channel encryption keys are derived via Elliptic Curve Diffie-Hellman, meaning the relay server never sees plaintext data.
|
||||
- **Per-channel authentication**: Each mobile session authenticates independently.
|
||||
- **Back-pressure**: Data limiters prevent channel flooding.
|
||||
- **License-gated**: Relay functionality requires a Pro or Cloud license.
|
||||
- **License-gated**: Relay functionality requires a Relay, Pro, legacy Pro+, or Cloud license.
|
||||
- **Configurable**: Enable/disable via **Settings → Relay** (admin only).
|
||||
|
||||
### Agent Command Security
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import {
|
|||
export const RelaySettingsPanel: Component<RelaySettingsPanelProps> = (props) => {
|
||||
const state = useRelaySettingsPanelState(props);
|
||||
|
||||
// Pro feature gate
|
||||
// Relay feature gate
|
||||
if (!state.relayEnabled()) {
|
||||
return (
|
||||
<SettingsPanel title="Remote Access" description={RELAY_SETTINGS_DESCRIPTION}>
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ describe('RelaySettingsPanel runtime', () => {
|
|||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Remote access is available with Relay or Pro. Pair supported Pulse Mobile clients with this instance using a QR code or deep link.',
|
||||
'Remote access is available with Relay and higher plans. Pair supported Pulse Mobile clients with this instance using a QR code or deep link.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'View plans' })).toHaveAttribute(
|
||||
|
|
@ -257,7 +257,7 @@ describe('RelaySettingsPanel runtime', () => {
|
|||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Remote Access is enabled, but this instance does not have an active Relay token. Activate Relay or turn Remote Access off before pairing mobile clients.',
|
||||
'Remote Access is enabled, but this instance does not have an active Relay token. Activate a Relay-capable plan or turn Remote Access off before pairing mobile clients.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('register: no license token available')).not.toBeInTheDocument();
|
||||
|
|
|
|||
|
|
@ -88,6 +88,21 @@ describe('systemSettings store', () => {
|
|||
expect(configurationDoc).toContain('PULSE_TELEMETRY');
|
||||
});
|
||||
|
||||
it('keeps Relay security guidance aligned with the Relay tier boundary', () => {
|
||||
const securityDoc = readFileSync(path.join(repoRoot, 'SECURITY.md'), 'utf8');
|
||||
const publicSecurityDoc = readFileSync(
|
||||
path.join(frontendRoot, 'public', 'docs', 'SECURITY.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
for (const copy of [securityDoc, publicSecurityDoc]) {
|
||||
expect(copy).toContain('Relay Security (Relay and Above)');
|
||||
expect(copy).toContain('Relay functionality requires a Relay, Pro, legacy Pro+, or Cloud license');
|
||||
expect(copy).not.toContain('Relay Security (Pro)');
|
||||
expect(copy).not.toContain('Relay functionality requires a Pro or Cloud license');
|
||||
}
|
||||
});
|
||||
|
||||
it('documents self-hosted AI provider transport and resource-policy redaction in the privacy doc', () => {
|
||||
const privacyDoc = readFileSync(path.join(repoRoot, 'docs', 'PRIVACY.md'), 'utf8');
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,25 @@ describe('quickstart copy contract', () => {
|
|||
const pulsePro = readRepoFile('docs/PULSE_PRO.md');
|
||||
const ai = readRepoFile('docs/AI.md');
|
||||
const privacy = readRepoFile('docs/PRIVACY.md');
|
||||
const security = readRepoFile('SECURITY.md');
|
||||
const publicPrivacy = readRepoFile('frontend-modern/public/docs/PRIVACY.md');
|
||||
const publicSecurity = readRepoFile('frontend-modern/public/docs/SECURITY.md');
|
||||
const pricingSpec = readRepoFile('docs/architecture/v6-pricing-and-tiering.md');
|
||||
const aiSettingsDialog = readRepoFile(
|
||||
'frontend-modern/src/components/Settings/AISettingsDialogs.tsx',
|
||||
);
|
||||
|
||||
for (const copy of [readme, pulsePro, ai, privacy, publicPrivacy, pricingSpec, aiSettingsDialog]) {
|
||||
for (const copy of [
|
||||
readme,
|
||||
pulsePro,
|
||||
ai,
|
||||
privacy,
|
||||
security,
|
||||
publicPrivacy,
|
||||
publicSecurity,
|
||||
pricingSpec,
|
||||
aiSettingsDialog,
|
||||
]) {
|
||||
expect(copy).not.toMatch(/quickstart/i);
|
||||
expect(copy).not.toContain('quickstart:pulse-hosted');
|
||||
expect(copy).not.toMatch(/hosted AI/i);
|
||||
|
|
@ -28,4 +40,21 @@ describe('quickstart copy contract', () => {
|
|||
expect(copy).not.toMatch(/Pulse Account[\s\S]{0,120}no API key/i);
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps Relay public docs aligned with the Relay tier instead of Pro-only copy', () => {
|
||||
const security = readRepoFile('SECURITY.md');
|
||||
const publicSecurity = readRepoFile('frontend-modern/public/docs/SECURITY.md');
|
||||
const screenshots = readRepoFile('docs/SCREENSHOTS.md');
|
||||
|
||||
for (const copy of [security, publicSecurity, screenshots]) {
|
||||
expect(copy).not.toContain('Relay Security (Pro)');
|
||||
expect(copy).not.toContain('Relay functionality requires a Pro or Cloud license');
|
||||
expect(copy).not.toContain('relay protocol (Pro feature)');
|
||||
}
|
||||
|
||||
expect(security).toContain('Relay Security (Relay and Above)');
|
||||
expect(publicSecurity).toContain('Relay Security (Relay and Above)');
|
||||
expect(security).toContain('Relay, Pro, legacy Pro+, or Cloud license');
|
||||
expect(publicSecurity).toContain('Relay, Pro, legacy Pro+, or Cloud license');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -106,12 +106,14 @@ describe('relayPresentation', () => {
|
|||
it('centralizes relay availability copy', () => {
|
||||
expect(RELAY_SETTINGS_DESCRIPTION).toContain('Pulse Mobile pairing');
|
||||
expect(RELAY_LICENSE_REQUIRED_MESSAGE).toContain('supported Pulse Mobile clients');
|
||||
expect(RELAY_LICENSE_REQUIRED_MESSAGE).toContain('available with Relay or Pro');
|
||||
expect(RELAY_LICENSE_REQUIRED_MESSAGE).toContain('available with Relay and higher plans');
|
||||
expect(RELAY_LICENSE_REQUIRED_MESSAGE).not.toContain('Relay or Pro');
|
||||
expect(RELAY_PAIRING_AVAILABILITY_TITLE).toBe('Pair Pulse Mobile through Relay');
|
||||
expect(RELAY_PAIRING_AVAILABILITY_MESSAGE).toContain('QR code or deep link');
|
||||
expect(RELAY_ENABLE_HELP_TEXT).toContain('Pulse Mobile pairing');
|
||||
expect(RELAY_ACTIVATION_REQUIRED_LABEL).toBe('Activation required');
|
||||
expect(RELAY_ACTIVATION_REQUIRED_MESSAGE).toContain('active Relay token');
|
||||
expect(RELAY_ACTIVATION_REQUIRED_MESSAGE).toContain('Relay-capable plan');
|
||||
});
|
||||
|
||||
it('does not retain retired Relay price or trial-era onboarding copy', () => {
|
||||
|
|
@ -121,5 +123,6 @@ describe('relayPresentation', () => {
|
|||
expect(relayPresentationSource).not.toContain('$49');
|
||||
expect(relayPresentationSource).not.toContain('$99');
|
||||
expect(relayPresentationSource).not.toContain('Start free trial');
|
||||
expect(relayPresentationSource).not.toContain('Pro feature gate');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const RELAY_DIAGNOSTICS_TITLE_CLASS = 'text-xs font-semibold text-base-co
|
|||
export const RELAY_SETTINGS_DESCRIPTION =
|
||||
'Configure Pulse relay connectivity for secure remote access and Pulse Mobile pairing.';
|
||||
export const RELAY_LICENSE_REQUIRED_MESSAGE =
|
||||
'Remote access is available with Relay or Pro. Pair supported Pulse Mobile clients with this instance using a QR code or deep link.';
|
||||
'Remote access is available with Relay and higher plans. Pair supported Pulse Mobile clients with this instance using a QR code or deep link.';
|
||||
export const RELAY_PAIRING_AVAILABILITY_TITLE = 'Pair Pulse Mobile through Relay';
|
||||
export const RELAY_PAIRING_AVAILABILITY_MESSAGE =
|
||||
'Supported Pulse Mobile clients connect to this Pulse instance with a QR code or deep link over end-to-end encrypted relay connectivity.';
|
||||
|
|
@ -39,7 +39,7 @@ export const RELAY_ENABLE_HELP_TEXT =
|
|||
'Connect this Pulse instance to the relay server for secure remote access and Pulse Mobile pairing.';
|
||||
export const RELAY_ACTIVATION_REQUIRED_LABEL = 'Activation required';
|
||||
export const RELAY_ACTIVATION_REQUIRED_MESSAGE =
|
||||
'Remote Access is enabled, but this instance does not have an active Relay token. Activate Relay or turn Remote Access off before pairing mobile clients.';
|
||||
'Remote Access is enabled, but this instance does not have an active Relay token. Activate a Relay-capable plan or turn Remote Access off before pairing mobile clients.';
|
||||
|
||||
export function getRelayDiagnosticClass(severity: 'warning' | 'error'): string {
|
||||
return severity === 'error'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue