diff --git a/apps/remix/app/components/forms/signup.tsx b/apps/remix/app/components/forms/signup.tsx index d94b1c9b65..59d3e654c0 100644 --- a/apps/remix/app/components/forms/signup.tsx +++ b/apps/remix/app/components/forms/signup.tsx @@ -411,19 +411,11 @@ export const SignUpForm = ({

By proceeding, you agree to our{' '} - + Terms of Service {' '} and{' '} - + Privacy Policy . diff --git a/apps/remix/app/routes/_share+/share.$slug.tsx b/apps/remix/app/routes/_share+/share.$slug.tsx index 4e8029f73e..17c9431b54 100644 --- a/apps/remix/app/routes/_share+/share.$slug.tsx +++ b/apps/remix/app/routes/_share+/share.$slug.tsx @@ -12,15 +12,15 @@ export function meta({ params: { slug } }: Route.MetaArgs) { } return [ - { title: 'Documenso - Share' }, - { description: 'I just signed a document in style with Documenso!' }, + { title: 'Crove Sign - Share' }, + { description: 'I just signed a document in style with Crove Sign!' }, { property: 'og:title', - content: 'Documenso - Join the open source signing revolution', + content: 'Crove Sign - Electronic signatures for the Crove OS ecosystem', }, { property: 'og:description', - content: 'I just signed with Documenso!', + content: 'I just signed with Crove Sign!', }, { property: 'og:type', @@ -32,7 +32,7 @@ export function meta({ params: { slug } }: Route.MetaArgs) { }, { name: 'twitter:site', - content: '@documenso', + content: '@crovedos', }, { name: 'twitter:card', @@ -44,7 +44,7 @@ export function meta({ params: { slug } }: Route.MetaArgs) { }, { name: 'twitter:description', - content: 'I just signed with Documenso!', + content: 'I just signed with Crove Sign!', }, ]; } @@ -69,8 +69,8 @@ export const loader = async ({ request, params: { slug } }: Route.LoaderArgs) => return {}; } - // Is hardcoded because this whole meta is hardcoded anyway for Documenso. - throw redirect('https://documenso.com'); + // Non-share visitors (bots excluded above) land on the app home. + throw redirect(NEXT_PUBLIC_WEBAPP_URL()); }; export default function SharePage() { diff --git a/apps/remix/app/routes/_unauthenticated+/privacy.tsx b/apps/remix/app/routes/_unauthenticated+/privacy.tsx new file mode 100644 index 0000000000..8f7b48110c --- /dev/null +++ b/apps/remix/app/routes/_unauthenticated+/privacy.tsx @@ -0,0 +1,97 @@ +import { Button } from '@documenso/ui/primitives/button'; +import { Trans } from '@lingui/react/macro'; +import { Link } from 'react-router'; + +// PLACEHOLDER LEGAL TEXT: replace with a privacy policy reviewed for the +// operating entity before relying on it commercially. +export default function PrivacyPolicy() { + return ( +

+
+

+ Privacy Policy +

+ +

+ + Last updated: September 2026. This policy explains what personal data Crove Sign processes, why, and the + choices you have. + +

+ +

+ 1. Data We Process +

+

+ + Account data (name, email, avatar) synced from your identity provider, the documents you upload, signature + and audit metadata, and technical logs such as IP address and user agent required to operate the service. + +

+ +

+ 2. Why We Process It +

+

+ + To provide the signing service, keep documents and audit trails verifiable, send you transactional emails + about your documents, prevent abuse, and comply with legal obligations. + +

+ +

+ 3. Document Confidentiality +

+

+ + Your documents are processed only to deliver the service. We do not use your document contents for + advertising or train models on them. + +

+ +

+ 4. Data Sharing +

+

+ + We share data only with the infrastructure providers that operate the Crove OS ecosystem, with recipients + and senders involved in a signing workflow, and where required by law. + +

+ +

+ 5. Retention +

+

+ + Documents and audit records are retained while your account is active and as required for the integrity of + completed signings. You can request deletion of your account and associated data. + +

+ +

+ 6. Your Rights +

+

+ + You can access, correct, export, or delete your personal data through your account settings or by contacting + support through the Crove OS ecosystem channels. + +

+ +

+ 7. Contact +

+

+ Privacy questions can be sent through the support channels of the Crove OS ecosystem. +

+
+ + + + +
+ ); +} diff --git a/apps/remix/app/routes/_unauthenticated+/terms.tsx b/apps/remix/app/routes/_unauthenticated+/terms.tsx new file mode 100644 index 0000000000..e82fca8ef4 --- /dev/null +++ b/apps/remix/app/routes/_unauthenticated+/terms.tsx @@ -0,0 +1,119 @@ +import { Button } from '@documenso/ui/primitives/button'; +import { Trans } from '@lingui/react/macro'; +import { Link } from 'react-router'; + +// PLACEHOLDER LEGAL TEXT: replace with terms reviewed for the operating +// entity before relying on them commercially. +export default function TermsOfService() { + return ( +
+
+

+ Terms of Service +

+ +

+ + Last updated: September 2026. These terms govern your use of Crove Sign, the electronic signature service + operated as part of the Crove OS ecosystem. + +

+ +

+ 1. Acceptance of Terms +

+

+ + By creating an account or using Crove Sign you agree to these Terms of Service. If you use the service on + behalf of an organisation, you confirm that you are authorised to bind that organisation. + +

+ +

+ 2. The Service +

+

+ + Crove Sign lets you prepare, send, sign, and manage documents electronically, including advanced electronic + signatures and optional blockchain-sealed attestation records. The service is provided through the web + application and its associated APIs. + +

+ +

+ 3. Your Account +

+

+ + You are responsible for safeguarding the credentials to your identity provider account, for the accuracy of + the information you provide, and for all activity carried out under your account. + +

+ +

+ 4. Your Documents and Content +

+

+ + You retain all rights to the documents you upload and the signatures you apply. You grant us only the + limited rights needed to store, process, and deliver those documents as part of providing the service. + +

+ +

+ 5. Legal Effect of Electronic Signatures +

+

+ + Electronic signatures carry legal effect under applicable law when the signatory intends to sign. You are + responsible for ensuring that your use of electronic signatures satisfies the legal requirements of your + jurisdiction and document type. + +

+ +

+ 6. Acceptable Use +

+

+ + You may not use the service to send unwanted signing requests, to mislead signers about what they are + signing, or for any unlawful purpose. + +

+ +

+ 7. Availability and Changes +

+

+ + We may modify or discontinue features, and may update these terms. Material changes will be communicated + through the service before taking effect. + +

+ +

+ 8. Limitation of Liability +

+

+ + To the maximum extent permitted by law, the service is provided "as is" and we are not liable for indirect + or consequential damages arising from your use of the service. + +

+ +

+ 9. Contact +

+

+ Questions about these terms can be sent through the support channels of the Crove OS ecosystem. +

+
+ + + + +
+ ); +} diff --git a/apps/remix/public/android-chrome-192x192.png b/apps/remix/public/android-chrome-192x192.png index 7613afd598..ad35dbc634 100644 Binary files a/apps/remix/public/android-chrome-192x192.png and b/apps/remix/public/android-chrome-192x192.png differ diff --git a/apps/remix/public/android-chrome-512x512.png b/apps/remix/public/android-chrome-512x512.png index 9d6675a6a4..96d8f5f7e6 100644 Binary files a/apps/remix/public/android-chrome-512x512.png and b/apps/remix/public/android-chrome-512x512.png differ diff --git a/apps/remix/public/apple-touch-icon.png b/apps/remix/public/apple-touch-icon.png index 22b756a89d..e3b94a7f42 100644 Binary files a/apps/remix/public/apple-touch-icon.png and b/apps/remix/public/apple-touch-icon.png differ diff --git a/apps/remix/public/favicon-16x16.png b/apps/remix/public/favicon-16x16.png index ddb28ae723..2ff3438a54 100644 Binary files a/apps/remix/public/favicon-16x16.png and b/apps/remix/public/favicon-16x16.png differ diff --git a/apps/remix/public/favicon-32x32.png b/apps/remix/public/favicon-32x32.png index 210dcb4d10..0734c47db8 100644 Binary files a/apps/remix/public/favicon-32x32.png and b/apps/remix/public/favicon-32x32.png differ diff --git a/apps/remix/public/favicon.ico b/apps/remix/public/favicon.ico index b295574836..6d60870ab5 100644 Binary files a/apps/remix/public/favicon.ico and b/apps/remix/public/favicon.ico differ diff --git a/apps/remix/public/opengraph-image.jpg b/apps/remix/public/opengraph-image.jpg index 7be4300005..b4e2f668e9 100644 Binary files a/apps/remix/public/opengraph-image.jpg and b/apps/remix/public/opengraph-image.jpg differ diff --git a/apps/remix/public/static/logo.png b/apps/remix/public/static/logo.png index 4813aaaef0..7f598d08d5 100644 Binary files a/apps/remix/public/static/logo.png and b/apps/remix/public/static/logo.png differ diff --git a/apps/remix/public/static/og-share-frame.png b/apps/remix/public/static/og-share-frame.png new file mode 100644 index 0000000000..e92f505532 Binary files /dev/null and b/apps/remix/public/static/og-share-frame.png differ diff --git a/apps/remix/public/static/og-share-frame2.png b/apps/remix/public/static/og-share-frame2.png index b9a65657da..4a2ebd6708 100644 Binary files a/apps/remix/public/static/og-share-frame2.png and b/apps/remix/public/static/og-share-frame2.png differ diff --git a/packages/assets/logo_icon.png b/packages/assets/logo_icon.png index cb4765fcf1..b9167e38ee 100644 Binary files a/packages/assets/logo_icon.png and b/packages/assets/logo_icon.png differ diff --git a/packages/assets/static/logo.png b/packages/assets/static/logo.png index 4813aaaef0..7f598d08d5 100644 Binary files a/packages/assets/static/logo.png and b/packages/assets/static/logo.png differ diff --git a/packages/assets/static/og-share-frame.png b/packages/assets/static/og-share-frame.png index 72bf7dd963..e92f505532 100644 Binary files a/packages/assets/static/og-share-frame.png and b/packages/assets/static/og-share-frame.png differ diff --git a/packages/assets/static/og-share-frame2.png b/packages/assets/static/og-share-frame2.png index b9a65657da..4a2ebd6708 100644 Binary files a/packages/assets/static/og-share-frame2.png and b/packages/assets/static/og-share-frame2.png differ diff --git a/packages/email/template-components/template-branding-logo.tsx b/packages/email/template-components/template-branding-logo.tsx index 6b560aed29..f3d2bf8400 100644 --- a/packages/email/template-components/template-branding-logo.tsx +++ b/packages/email/template-components/template-branding-logo.tsx @@ -23,7 +23,7 @@ export const TemplateBrandingLogo = ({ assetBaseUrl, className = 'mb-4 h-6' }: T if (!hasCustomBrandingLogo) { const documensoLogoUrl = getEmailAssetUrl(assetBaseUrl, 'static/logo.png'); - return Documenso Logo; + return Crove Sign Logo; } const brandingLogo = Branding Logo; diff --git a/packages/email/template-components/template-footer.tsx b/packages/email/template-components/template-footer.tsx index ed82efb783..4ca0f94c67 100644 --- a/packages/email/template-components/template-footer.tsx +++ b/packages/email/template-components/template-footer.tsx @@ -1,3 +1,4 @@ +import { APP_COMPANY_DETAILS, NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { Trans } from '@lingui/react/macro'; import { Fragment } from 'react'; @@ -33,7 +34,7 @@ export const TemplateFooter = ({ isDocument = true, reportUrl }: TemplateFooterP This document was sent using{' '} - + Documenso . @@ -64,9 +65,16 @@ export const TemplateFooter = ({ isDocument = true, reportUrl }: TemplateFooterP {!branding.brandingEnabled && ( - Documenso, Inc. -
- 2261 Market Street, #5211, San Francisco, CA 94114, USA + {APP_COMPANY_DETAILS() + .split('\n') + .map((line, idx) => { + return ( + + {idx > 0 &&
} + {line} +
+ ); + })}
)} diff --git a/packages/email/templates/admin-user-created.tsx b/packages/email/templates/admin-user-created.tsx index 000b4d3cbd..45f434532a 100644 --- a/packages/email/templates/admin-user-created.tsx +++ b/packages/email/templates/admin-user-created.tsx @@ -24,7 +24,7 @@ export const AdminUserCreatedTemplate = ({
- Documenso Logo + Crove Sign Logo
diff --git a/packages/lib/constants/app.ts b/packages/lib/constants/app.ts index 8ab113b738..7a47b2021d 100644 --- a/packages/lib/constants/app.ts +++ b/packages/lib/constants/app.ts @@ -184,3 +184,10 @@ export const CSC_INSTANCE_SIGNATURE_LEVEL = (): TSignatureLevel => { }; export const DOCUMENSO_CLOUD_ENTERPRISE_CTA_URL = 'https://documen.so/enterprise-cta'; + +/** + * Company identity rendered in the email footer when an organisation has no + * custom branding. Override per deployment (legal address, site, etc.) via + * the NEXT_PRIVATE_BRANDING_COMPANY_DETAILS env (newline-separated lines). + */ +export const APP_COMPANY_DETAILS = () => env('NEXT_PRIVATE_BRANDING_COMPANY_DETAILS') || 'Crove\nhttps://crove.com'; diff --git a/scripts/patch-crove-branding.mjs b/scripts/patch-crove-branding.mjs index eb3cd23b59..565807183d 100644 --- a/scripts/patch-crove-branding.mjs +++ b/scripts/patch-crove-branding.mjs @@ -294,11 +294,169 @@ export const BrandingLogoIcon = ({ className = 'h-6 w-auto', ...props }: LogoPro } // ========================================== -// 5. UPDATE METADATA UTILS +// 5. RASTERIZE BRAND BINARIES +// ========================================== + +// Regenerates the committed PNG/ICO/JPG brand binaries from the Crove SVG +// (the upstream Documenso artwork that ships with the repo would otherwise +// keep leaking into favicons, social previews and email footers). Requires +// sharp at the repo root; skipped gracefully when unavailable so the patch +// never fails a fresh clone without node_modules. +async function rasterizeBrandBinaries() { + console.log('🖼️ [4/5] Rasterizing Crove Sign brand binaries...'); + + let sharp; + + try { + sharp = (await import('sharp')).default; + } catch { + console.log(' ⚠️ sharp not available - skipping binary rasterization (committed binaries kept)'); + return; + } + + const MARK_SVG = ` + + +`; + + const GREEN = '#10B981'; + const DARK = '#0f1113'; + + const wordmarkSvg = ( + textColor, + bg, + ) => ` + ${bg ? `` : ''} + Crove + Sign +`; + + const ogSvg = ` + + + ${MARK_SVG.replace('', '').replace('', '')} + Crove + Sign + Electronic signatures for the Crove OS ecosystem +`; + + const shareFrameSvg = ( + accent, + ) => ` + + + Crove + Sign + ${MARK_SVG.replace('', '').replace('', '')} +`; + + // Minimal ICO container wrapping a single PNG-compressed image (valid on + // Windows Vista and every browser released since). + const pngToIco = (pngBuffer) => { + const header = Buffer.alloc(6); + header.writeUInt16LE(0, 0); + header.writeUInt16LE(1, 2); + header.writeUInt16LE(1, 4); + + const entry = Buffer.alloc(16); + entry.writeUInt8(32, 0); + entry.writeUInt8(32, 1); + entry.writeUInt8(0, 2); + entry.writeUInt8(0, 3); + entry.writeUInt16LE(1, 4); + entry.writeUInt16LE(32, 6); + entry.writeUInt32LE(pngBuffer.length, 8); + entry.writeUInt32LE(22, 12); + + return Buffer.concat([header, entry, pngBuffer]); + }; + + const writeIco = async (buffer) => { + const png = await sharp(buffer).resize(32, 32).png().toBuffer(); + return pngToIco(png); + }; + + const targets = [ + ['apps/remix/public/favicon-16x16.png', 16], + ['apps/remix/public/favicon-32x32.png', 32], + ['apps/remix/public/apple-touch-icon.png', 180], + ['apps/remix/public/android-chrome-192x192.png', 192], + ['apps/remix/public/android-chrome-512x512.png', 512], + ]; + + try { + for (const [relPath, size] of targets) { + await sharp(Buffer.from(MARK_SVG)).resize(size, size).png().toFile(path.join(ROOT_DIR, relPath)); + console.log(` ✓ ${relPath} (${size}x${size})`); + } + + fs.writeFileSync(path.join(ROOT_DIR, 'apps/remix/public/favicon.ico'), await writeIco(Buffer.from(MARK_SVG))); + console.log(' ✓ apps/remix/public/favicon.ico (32x32 PNG-ICO)'); + + await sharp(Buffer.from(wordmarkSvg('#111311', null))) + .png() + .toFile(path.join(ROOT_DIR, 'packages/assets/static/logo.png')); + console.log(' ✓ packages/assets/static/logo.png (374x55 wordmark)'); + + // The SERVED copies live in apps/remix/public/static/ (email asset-url + // and the share OG composition fetch them over HTTP) - packages/assets/ + // static/ is the source-of-record twin. + await sharp(Buffer.from(wordmarkSvg('#111311', null))) + .png() + .toFile(path.join(ROOT_DIR, 'apps/remix/public/static/logo.png')); + console.log(' ✓ apps/remix/public/static/logo.png (374x55 wordmark)'); + + await sharp(Buffer.from(MARK_SVG)) + .resize(320, 320) + .png() + .toFile(path.join(ROOT_DIR, 'packages/assets/logo_icon.png')); + console.log(' ✓ packages/assets/logo_icon.png (320x320)'); + + await sharp(Buffer.from(ogSvg)) + .jpeg({ quality: 90 }) + .toFile(path.join(ROOT_DIR, 'apps/remix/public/opengraph-image.jpg')); + console.log(' ✓ apps/remix/public/opengraph-image.jpg (1270x760)'); + + await sharp(Buffer.from(shareFrameSvg(GREEN))) + .png() + .toFile(path.join(ROOT_DIR, 'packages/assets/static/og-share-frame.png')); + console.log(' ✓ packages/assets/static/og-share-frame.png (1200x630)'); + + await sharp(Buffer.from(shareFrameSvg(GREEN))) + .png() + .toFile(path.join(ROOT_DIR, 'apps/remix/public/static/og-share-frame.png')); + console.log(' ✓ apps/remix/public/static/og-share-frame.png (1200x630)'); + + await sharp(Buffer.from(shareFrameSvg('#34D399'))) + .png() + .toFile(path.join(ROOT_DIR, 'packages/assets/static/og-share-frame2.png')); + console.log(' ✓ packages/assets/static/og-share-frame2.png (1200x630)'); + + await sharp(Buffer.from(shareFrameSvg('#34D399'))) + .png() + .toFile(path.join(ROOT_DIR, 'apps/remix/public/static/og-share-frame2.png')); + console.log(' ✓ apps/remix/public/static/og-share-frame2.png (1200x630)'); + } catch (error) { + // Degrade to the committed binaries on any sharp/libvips failure rather + // than failing the whole patch run. + console.log(` ⚠️ Rasterization failed (${error.message}) - committed binaries kept`); + } + + console.log(' ✅ Brand binaries rasterized.\n'); +} + +// ========================================== +// 6. UPDATE METADATA UTILS // ========================================== function patchMetadata() { - console.log('📄 [4/4] Verifying app metadata...'); + console.log('📄 [5/5] Verifying app metadata...'); const metaPath = path.join(ROOT_DIR, 'apps', 'remix', 'app', 'utils', 'meta.ts'); const metaContent = `import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { i18n, type MessageDescriptor } from '@lingui/core'; @@ -372,7 +530,7 @@ export const appMetaTags = (title?: MessageDescriptor) => { // MAIN RUNNER // ========================================== -function main() { +async function main() { console.log('\n========================================'); console.log('🚀 Crove Sign - Enterprise Brand Patch'); console.log('========================================\n'); @@ -381,6 +539,7 @@ function main() { patchTranslationCatalogs(); patchWebManifests(); generateBrandAssets(); + await rasterizeBrandBinaries(); patchMetadata(); console.log('🎉 ALL CROVE SIGN BRANDING PATCHES APPLIED SUCCESSFULLY!\n');