Skip to content

Adding encrypted INCY links - #53

Open
densds wants to merge 11 commits into
remnawave:devfrom
densds:dev
Open

Adding encrypted INCY links#53
densds wants to merge 11 commits into
remnawave:devfrom
densds:dev

Conversation

@densds

@densds densds commented Jul 18, 2026

Copy link
Copy Markdown

Moved subscription encryption from the backend to the frontend, modified the original INCY library, and now all logic is moved to the frontend without public API endpoints

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves INCY encrypted deep-link generation entirely to the frontend by introducing a useEffect that calls encryptLink from the new @densds/link-encoder package, adding an {{INCY_CRYPT1_LINK}} placeholder analogous to the existing HAPP_CRYPT3/4_LINK placeholders. The docker-compose.yml is also updated with container naming, restart policy, and an external network reference.

  • INCY client-side encryption (installation-guide.connector.tsx): encryptLink is called on every component mount regardless of whether any button actually uses the INCY placeholder; the subscriptionLink case now uses window.location.href instead of window.open(..., '_blank'), which navigates the current tab for any subscriptionLink-type button (not just INCY deep links).
  • docker-compose external network: remnawave-network is declared external: true, requiring the network to exist before docker-compose up — users without a pre-created network will get a fatal startup error.
  • New npm dependency @densds/link-encoder ^2.0.0 is published under the same namespace as the PR author with no independently verifiable source repository.

Confidence Score: 3/5

The docker-compose change introduces a hard runtime dependency on a pre-existing external network, which will break docker-compose up for users who pull this change without advance notice.

The docker-compose external: true network declaration is a breaking operational change — any environment without remnawave-network already created will fail to start the container. Combined with the frontend connector carrying the window.location.href regression for subscriptionLink buttons and the unconditional encryptLink call on every mount, there are multiple real defects on changed paths that warrant caution before merging.

docker-compose.yml (external network prerequisite) and frontend/src/widgets/main/installation-guide/installation-guide.connector.tsx (navigation regression, unconditional encryption effect).

Important Files Changed

Filename Overview
docker-compose.yml Adds container/hostname/restart/network config; marks remnawave-network as external: true, which breaks docker-compose up for any deployment without a pre-created network.
frontend/src/widgets/main/installation-guide/installation-guide.connector.tsx Adds INCY encrypted link generation via a client-side useEffect; introduces window.location.href for subscriptionLink buttons (regression for https links) and runs encryptLink unconditionally on every mount.
frontend/package.json Adds @densds/link-encoder ^2.0.0 dependency; package is authored by this PR's author with limited independent verifiability.
README.md Documents the INCY encrypted link feature and its setup steps; does not mention the new remnawave-network prerequisite in docker-compose.
backend/src/modules/root/root.controller.ts Trailing newline removed only; no logic changes.
backend/src/modules/root/root.service.ts Trailing newline removed only; no logic changes.
backend/package.json Trailing newline removed only; no dependency changes.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Browser
    participant InstallationGuideConnector
    participant encryptLink as encryptLink (@densds/link-encoder)
    participant OS as OS/App Handler

    Browser->>InstallationGuideConnector: Mount (subscriptionUrl, username)
    InstallationGuideConnector->>InstallationGuideConnector: "incyCryptLoading = true"
    InstallationGuideConnector->>encryptLink: "encryptLink(subscriptionUrl, { name })"
    encryptLink-->>InstallationGuideConnector: incy://crypt1/... (or error)
    InstallationGuideConnector->>InstallationGuideConnector: "setIncyCryptLink(link) / incyCryptLoading = false"

    Browser->>InstallationGuideConnector: User clicks INCY button
    alt incyCryptLink is undefined
        InstallationGuideConnector-->>Browser: Show error notification
    else incyCryptLink is ready
        InstallationGuideConnector->>Browser: "window.location.href = incyCryptLink"
        Browser->>OS: Deep link handoff (incy://)
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Browser
    participant InstallationGuideConnector
    participant encryptLink as encryptLink (@densds/link-encoder)
    participant OS as OS/App Handler

    Browser->>InstallationGuideConnector: Mount (subscriptionUrl, username)
    InstallationGuideConnector->>InstallationGuideConnector: "incyCryptLoading = true"
    InstallationGuideConnector->>encryptLink: "encryptLink(subscriptionUrl, { name })"
    encryptLink-->>InstallationGuideConnector: incy://crypt1/... (or error)
    InstallationGuideConnector->>InstallationGuideConnector: "setIncyCryptLink(link) / incyCryptLoading = false"

    Browser->>InstallationGuideConnector: User clicks INCY button
    alt incyCryptLink is undefined
        InstallationGuideConnector-->>Browser: Show error notification
    else incyCryptLink is ready
        InstallationGuideConnector->>Browser: "window.location.href = incyCryptLink"
        Browser->>OS: Deep link handoff (incy://)
    end
Loading

Reviews (2): Last reviewed commit: "Transferring encrypted links from the ba..." | Re-trigger Greptile

Comment on lines 152 to 156
case 'subscriptionLink': {
if (!formattedUrl) return

window.open(formattedUrl, '_blank')
window.location.href = formattedUrl
break

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 subscriptionLink now navigates in-place for all subscription buttons

The change from window.open(formattedUrl, '_blank') to window.location.href = formattedUrl applies to every button with type 'subscriptionLink', not just INCY ones. Any subscription-link button that resolves to a regular https:// URL will now silently navigate the current tab away from the subscription page, discarding the loaded session. Only deep-link protocols (e.g. incy://, vpn://) are unaffected because the browser hands them off to the OS without actually navigating. If the intent is to support deep links from a subscriptionLink button, a narrower guard (e.g. checking the resulting URL protocol) would prevent the regression for https links.

Comment on lines +88 to +110
useEffect(() => {
let cancelled = false
setIncyCryptLoading(true)

// name is capped at 128 chars per @densds/link-encoder's encryptLink contract
const name = subscription.user.username.slice(0, 128)

encryptLink(subscriptionUrl, { name })
.then((link) => {
if (!cancelled) setIncyCryptLink(link)
})
.catch((e) => {
console.error('Failed to generate INCY link', e)
})
.finally(() => {
if (!cancelled) setIncyCryptLoading(false)
})

return () => {
cancelled = true
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [subscriptionUrl, subscription.user.username])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 encryptLink runs unconditionally even when INCY is not configured

encryptLink is called on every mount regardless of whether any button in the current config actually uses {{INCY_CRYPT1_LINK}}. For the majority of users who have no INCY button, this triggers an unnecessary async crypto operation and briefly sets incyCryptLoading to true. If the call also fails (network issue, library error), incyCryptLink stays undefined and any user who somehow triggers the button sees an error notification instead of a graceful no-op. Guarding the effect with a check before calling encryptLink avoids the waste and prevents a spurious loading flash for non-INCY deployments.

Comment thread frontend/package.json
},
"dependencies": {
"@gfazioli/mantine-spinner": "^2.3.9",
"@densds/link-encoder": "^2.0.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security Unverified npm package @densds/link-encoder from PR author

@densds/link-encoder is published under the same namespace as this PR's author (densds) and returns no results in standard npm-registry searches. Unlike the existing @kastov/cryptohapp dependency (which is independently verifiable), this package has no visible GitHub repository, changelog, or community adoption. Since it handles subscription URL encryption on the client side — processing real user subscription URLs — it sits in a sensitive position. Before merging, the project maintainers should verify the package's source code and confirm its npm publish provenance matches the author's identity.

@densds

densds commented Jul 18, 2026

Copy link
Copy Markdown
Author

@greptile-apps re-review

densds added 2 commits August 1, 2026 12:31
…render

Telegram Desktop's embedded WebView2 blocks navigation to custom schemes
(happ://, incy://) even on a fully synchronous click. When the page is
opened as a Telegram Mini App, immediately hand off to the system browser
via Telegram.WebApp.openLink() when platform is 'tdesktop', where deep
links work normally. No-op everywhere else, including mobile clients.
window.location.href was silently breaking the OS hand-off for custom-scheme
deep links inside Telegram's mobile Mini App WebView. window.open, called
synchronously with no async gap, is the pattern already validated in the
sibling remnawave-shopbot project for the same happ:// / incy:// links.
Regular http(s) subscriptionLink buttons also stay in a new tab now, same
fix as before, just via a single code path instead of a protocol branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants