Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ variables (`infra/test` enforces the list): a hash that differs between laptops
| `SIGN_PUBLIC_URL` | api: origin of Sign's signing app (`apps/sign`, its own registrable domain); signing session links point there. Default `http://localhost:3004` outside production; unset in production → creating a session answers 503 |
| `SIGN_DOCUMENTS_BUCKET` | api, worker, sign: Sign's documents bucket (S3, with `AWS_REGION`; Terraform output `sign_documents_bucket`). Unset: the fake store (Postgres) outside production, Sign off in production |
| `SIGN_NOTIFICATIONS_FROM` | worker: coss.com's sender of Sign emails ("<requester> via coss.com") for environments without their own `email_from`, on a verified domain of `NOTIFICATIONS_ENVIRONMENT_ID`'s organization; default `NOTIFICATIONS_FROM`'s address. The worker also reads `SIGN_PUBLIC_URL` (emailed links) |
| `SIGN_SEALER`, `SIGN_SEAL_KMS_KEY_ID`, `SIGN_SEAL_CERTIFICATE_CHAIN`, `SIGN_TSA_URL` | worker: what seals Sign's completed documents (`local`, the default outside production: a self-signed key made at start; `kms`: the KMS key and its certificate chain, PEM leaf first, `\n` accepted; `csc`: the CA's cloud signing API, below) and an optional RFC 3161 TSA (B-T timestamps; production: DigiCert's). Unset in production: completions wait. Shape in `@coss/sign/env` |
| `SIGN_SEALER`, `SIGN_SEAL_KMS_KEY_ID`, `SIGN_SEAL_CERTIFICATE_CHAIN`, `SIGN_TSA_URL` | worker: what seals Sign's completed documents (`local`, the default outside production: a self-signed key made at start; `kms`: the KMS key and its certificate chain, PEM leaf first, `\n` accepted; `csc`: the CA's cloud signing API, below) and an optional RFC 3161 TSA (B-T timestamps; production: DigiCert's). Production: `csc` (SSL.com eSealing); unset there, completions wait. Shape in `@coss/sign/env` |
| `SIGN_CSC_URL`, `SIGN_CSC_TOKEN_URL`, `SIGN_CSC_CLIENT_ID`, `SIGN_CSC_CLIENT_SECRET`, `SIGN_CSC_USERNAME`, `SIGN_CSC_PASSWORD`, `SIGN_CSC_CREDENTIAL_ID`, `SIGN_CSC_TOTP_SECRET` | worker with `SIGN_SEALER=csc`: a Cloud Signature Consortium API (SSL.com eSigner eSealing: `https://cs.ssl.com/csc/v1`, token `https://login.ssl.com/oauth2/token`; sandbox `cs-try.ssl.com`, `oauth-sandbox.ssl.com`), its OAuth password grant (client id, optional secret, account, password), the credential, and a base64 TOTP secret only for credentials that want a one-time password. Production: the secret ones in the stage secret (else `unset_secrets`) |
| `FORMS_PUBLIC_URL` | api, worker, web: the origin forms are served on (`<it>/f/<id>`; a registrable domain of its own in production, routed to the api service, which then serves `/f/*` only for that host). Default `http://localhost:3001` outside production; unset in production → Forms is off (503 `forms_unavailable`) |
| `FORMS_FILES_BUCKET` | api, worker, web: Forms' uploads (S3, with `AWS_REGION`). Unset: the fake store (Postgres) outside production; in production forms refuse files |
Expand Down Expand Up @@ -812,8 +812,7 @@ and SMTP credentials, dedicated IP pools for sub-accounts, tracking and receivin
endpoint filters by sub-account, budgets per sub-account, a portal for end users, MCP support, rollups of
sub-account reputation counts (each evaluation counts its window live), and sub-account tenant pause webhooks,
Sign's archival timestamps in seals (PAdES B-LTA: renewing a document timestamp before its TSA certificate expires) and a standalone certificate download,
a price for `sign.completed_requests`, Sign in the iOS app and the admin console, and Sign
in production (off until "Going live" in `packages/sign/AGENTS.md` is done),
a price for `sign.completed_requests`, and Sign in the iOS app and the admin console,
Forms in the iOS app, malware scanning of form uploads, conditional or multi-page forms, a drag-and-drop form builder,
per-form webhook URLs (endpoints filter by event type), custom domains for hosted forms,
sending from inboxes at coss.com's managed inbound domain in live environments (they only receive), inbox
Expand Down
21 changes: 16 additions & 5 deletions infra/terraform/production.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ deploy_actor_ids = ["2538462", "8019099", "25907159", "55134778", "343308169", "
# Optional secrets not stored in coss/production: take a name out once put-secret.sh has set it.
unset_secrets = [
"IPINFO_PRIVACY_TOKEN",
# Sign's CSC sealer (SSL.com eSigner): until its account exists.
"SIGN_CSC_CLIENT_SECRET", "SIGN_CSC_PASSWORD", "SIGN_CSC_TOTP_SECRET",
# Sign's CSC sealer: SSL.com's eSealing credential needs no one-time password.
"SIGN_CSC_TOTP_SECRET",
]
initial_image_tag = "2a8195a759b0f11d430f3e9cebfcf535a99dbca7"
log_retention_days = 30
Expand Down Expand Up @@ -69,6 +69,7 @@ plain_env = {
TURNSTILE_SITE_KEY = "0x4AAAAAAFU8QY0DX4ZJGoSK"
PORTAL_PUBLIC_URL = "https://portal.cosshooks.com"
SIGN_PUBLIC_URL = "https://cosssign.com"
SIGN_DOCUMENTS_BUCKET = "coss-production-sign-documents-294417174674"
SCHEDULING_PUBLIC_URL = "https://cossbook.com"
# Forms (`/f/*` on its own domain, routed to the api) and its uploads bucket.
FORMS_PUBLIC_URL = "https://cossforms.com"
Expand Down Expand Up @@ -131,6 +132,7 @@ plain_env = {
}
sign = {
DATABASE_POOL_MAX = "5"
SIGN_DOCUMENTS_BUCKET = "coss-production-sign-documents-294417174674"
AWS_REGION = "us-east-2"
VALKEY_CLUSTER = "true"
RATE_LIMIT_KEY_PREFIX = "coss"
Expand All @@ -146,6 +148,7 @@ plain_env = {
TRACKING_PUBLIC_URL = "https://track.cosslinks.com"
PORTAL_PUBLIC_URL = "https://portal.cosshooks.com"
SIGN_PUBLIC_URL = "https://cosssign.com"
SIGN_DOCUMENTS_BUCKET = "coss-production-sign-documents-294417174674"
SCHEDULING_PUBLIC_URL = "https://cossbook.com"
GOOGLE_CALENDAR_CLIENT_ID = "776019028945-dnuug57hi1tsjfdbu93evm8ggp1ge00q.apps.googleusercontent.com"
MICROSOFT_CALENDAR_CLIENT_ID = "e58a0c0d-e1bb-4aef-a861-43c755d96761"
Expand Down Expand Up @@ -232,9 +235,17 @@ plain_env = {
# Account and reputation emails go out through coss.com itself, from its own organization.
NOTIFICATIONS_ENVIRONMENT_ID = "env_01m4s0af5zent8fzxwmtkergw5"
NOTIFICATIONS_FROM = "coss.com <notifications@coss.com>"
# Sign's seal timestamps (PAdES B-T): DigiCert's public TSA, whose chain reaches an unrestricted
# AATL root. Unused until SIGN_SEALER is set.
SIGN_TSA_URL = "http://timestamp.digicert.com"
# Sign's seal: Cal.com, Inc.'s OV document signing certificate in SSL.com eSigner, converted to
# eSealing (the client secret and password are in the secret). Timestamps (PAdES B-T): DigiCert's
# public TSA, whose chain reaches an unrestricted AATL root.
SIGN_DOCUMENTS_BUCKET = "coss-production-sign-documents-294417174674"
SIGN_SEALER = "csc"
SIGN_CSC_URL = "https://cs.ssl.com/csc/v1"
SIGN_CSC_TOKEN_URL = "https://login.ssl.com/oauth2/token"
SIGN_CSC_CLIENT_ID = "2SMQFRewdWcDsf1Nv2Fjh69pFNrwNtYJ7TxtR8AS6pQ"
SIGN_CSC_USERNAME = "pumfleet"
SIGN_CSC_CREDENTIAL_ID = "ed56283f-eb6f-4f30-a958-f0e87783a900"
SIGN_TSA_URL = "http://timestamp.digicert.com"
# Push notifications to the iOS app: the "coss.com push" key (production, topic com.coss.ios).
APNS_KEY_ID = "WCQDCU4HUL"
APNS_TEAM_ID = "ADWKXDJV75"
Expand Down
32 changes: 17 additions & 15 deletions packages/sign/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,22 +175,24 @@ templates and the embedded builder. See "Not built yet" in the root `AGENTS.md`

## Going live (production)

Sign is off in production until all of this is done, in this order:
Sign is on in production since these steps (in this order):

1. **Domain (done):** `cosssign.com` (self-serve Cloudflare account) → the `sign` service (README,
"Deploying to AWS"); `SIGN_PUBLIC_URL=https://cosssign.com` (web, api, worker).
2. **Bucket (done):** `sign.tf` is applied: `coss-production-sign-documents-294417174674` and
both KMS keys (the seal's is `alias/coss-production-sign-seal`).
3. **Seal:** a document-signing certificate from a CA on Adobe's trust list (AATL), either for
the KMS key (its CSR is signed by KMS: get the public key, sign the request's SHA-256 with
`kms sign --message-type DIGEST`; asked of Sectigo, since AATL CAs want key attestation KMS
can't give) → `SIGN_SEALER=kms`, `SIGN_SEAL_KMS_KEY_ID`, `SIGN_SEAL_CERTIFICATE_CHAIN`; or an
OV document signing certificate in SSL.com eSigner converted to eSealing ("Remove 2FA") →
`SIGN_SEALER=csc` with `SIGN_CSC_*` (worker; the password and any client secret through
`put-secret.sh`, then out of `unset_secrets`). `SIGN_TSA_URL` is set (DigiCert). Before
switching on, seal a test PDF with the same values (`bun run --cwd packages/sign seal:test
--out <file>`) and open it in Acrobat: signer trusted, timestamp trusted, "LTV enabled".
4. **Switch on:** set `SIGN_DOCUMENTS_BUCKET` (web, api, worker, sign), optionally
`SIGN_NOTIFICATIONS_FROM` (default `NOTIFICATIONS_FROM`'s address), `terraform apply`, deploy.
5. **Before announcing:** get legal review of `DEFAULT_SIGN_DISCLOSURE` and the certificate
wording, and price `sign.completed_requests` with a new rate card.
both KMS keys (the seal's is `alias/coss-production-sign-seal`, unused: AATL CAs want key
attestation KMS can't give).
3. **Seal (done):** Cal.com, Inc.'s OV document signing certificate (SSL.com, order
`co-5b1lenc0l11`, valid until 2027-11-06) in eSigner, converted to eSealing ("Remove 2FA",
`authMode: implicit`) → `SIGN_SEALER=csc` with `SIGN_CSC_*` (worker; an OAuth application at
login.ssl.com, the account `pumfleet`; the client secret and password in the stage secret).
`SIGN_TSA_URL` is DigiCert's. A test PDF sealed with the same values (`bun run --cwd
packages/sign seal:test --out <file>`) validates (pyHanko: trusted to SSL.com's root, DigiCert
timestamp, OCSP in the DSS) and opens in Acrobat as trusted and "LTV enabled". **Renew the
certificate before it expires**: a new certificate is a new credential id
(`SIGN_CSC_CREDENTIAL_ID`); seal a test PDF with it before switching.
4. **Switch on (done):** `SIGN_DOCUMENTS_BUCKET` (web, api, worker, sign), `terraform apply`,
deploy. `SIGN_NOTIFICATIONS_FROM` is unset (`NOTIFICATIONS_FROM`'s address).
5. **Before announcing:** legal reviewed `DEFAULT_SIGN_DISCLOSURE` and the certificate wording
(2026-11-06; change them only with another review); `sign.completed_requests` still needs a
price (a new rate card).
Loading