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
37 changes: 37 additions & 0 deletions docs/DEPLOY-KUBERNETES.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,39 @@ kubectl create secret generic backend-secret \
> **Note:** After rotating secrets, restart the workloads so the new values are picked up:
> `kubectl rollout restart deployment/backend -n synapsvault`.

### Managing `backend-secret` with External Secrets Operator

Instead of creating the secret by hand, you can let the [External Secrets Operator (ESO)](https://external-secrets.io/) sync it from your secret backend (AWS Secrets Manager, Vault, GCP Secret Manager, Azure Key Vault, ...).

1. **Install ESO** into the cluster:

```sh
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets --create-namespace
```

2. **Create a `SecretStore`/`ClusterSecretStore`** that points at your secret backend and holds the credentials ESO uses to read it. See the [ESO provider docs](https://external-secrets.io/latest/provider/aws-secrets-manager/) for the exact spec of your provider.

3. **Store the backend values** under a single remote secret (e.g. `synapsvault/backend`) with one property per key listed in `k8s/secrets.yaml` (`DATABASE_URL`, `JWT_SECRET`, `SESSION_SECRET`, ...).

4. **Apply the example manifest**, adjusting `secretStoreRef` and the `remoteRef` keys to match your store:

```sh
kubectl apply -f k8s/examples/external-secret.yaml
```

[`k8s/examples/external-secret.yaml`](../k8s/examples/external-secret.yaml) creates an `ExternalSecret` that materializes the `backend-secret` Secret with the same keys the backend expects. It is intentionally **not** part of `k8s/kustomization.yaml`, so `kubectl apply -k k8s/` never touches it.

5. **Verify the sync**:

```sh
kubectl get externalsecret backend-secret -n synapsvault
kubectl get secret backend-secret -n synapsvault
```

The `ExternalSecret` should report `SecretSynced` and the `backend-secret` Secret should exist. ESO refreshes it on the `refreshInterval` set in the manifest; restart the workloads after a rotation as noted above.

## 2. Apply the manifests

Build and apply everything under `k8s/` with kustomize:
Expand Down Expand Up @@ -285,6 +318,10 @@ If the backend signing key (`BACKEND_SECRET_KEY` / `STELLAR_SECRET_KEY`) or the

## Troubleshooting

- **Pods stuck in `Pending`** — check node capacity and scheduling constraints (`kubectl describe pod <pod> -n synapsvault`).
- **Pods in `CrashLoopBackOff`** — inspect logs (`kubectl logs <pod> -n synapsvault`) and confirm `backend-secret` exists with all required keys.
- **`ImagePullBackOff`** — verify the image tags and that the cluster can pull from `ghcr.io`.
- **Ingress returns 404/502** — confirm the ingress controller is running and the `Ingress` host matches your DNS.
- **Pods stuck in `Pending`** — check node capacity and scheduling constraints with `kubectl describe pod <pod> -n synapsvault`; look for insufficient CPU/memory or unsatisfied node selectors/taints.
- **Pods in `ImagePullBackOff`/`ErrImagePull`** — confirm the image tag exists in `ghcr.io/synapsvault/deploy/*` and that the cluster can pull from GHCR (image pull secret if the packages are private).
- **`CrashLoopBackOff`** — inspect `kubectl logs <pod> -n synapsvault --previous` and the initContainer logs (the `migrate` container runs first).
Expand Down
37 changes: 37 additions & 0 deletions docs/contract-deploy-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,43 @@ Kubernetes ConfigMap. Restart the backend afterwards to pick up the change.

---

## Updating the backend ConfigMap after a contract deploy

The backend ConfigMap (`k8s/configmap.yaml`) tracks the contract IDs in Git so
that `kubectl apply` and the running config never drift. The keys are:

```yaml
STELLAR_CONTRACT_VAULT_REGISTRY: "CC..."
STELLAR_CONTRACT_ACCESS_LEASE: "CD..."
STELLAR_CONTRACT_SUBSCRIPTION: "CE..."
```

After a contract deploy, update them in Git and re-apply:

```bash
# 1. Deploy (writes contract-ids.env) — see the automated section above
npm run deploy:contracts -- --network testnet

# 2. Copy the new IDs from contract-ids.env into k8s/configmap.yaml
# (replace the placeholder values for the three STELLAR_CONTRACT_* keys)

# 3. Commit the ConfigMap change so Git stays the source of truth
git add k8s/configmap.yaml
git commit -m "chore: update contract IDs in backend ConfigMap"

# 4. Apply and restart the backend to pick up the new IDs
kubectl apply -f k8s/configmap.yaml
kubectl rollout restart deployment/backend
```

> **Rollback:** `npm run rollback:contracts -- --backup contract-ids.env.<timestamp>
> --configmap backend-config` merge-patches the live ConfigMap. Always mirror the
> resulting IDs back into `k8s/configmap.yaml` and commit, otherwise the next
> `kubectl apply` will revert the running config to the stale Git values.

---

## Mainnet deployment safety checks
## Mainnet deployment safety checks & production requirements

While the quickstart steps below focus on **Testnet**, deploying to **Stellar Mainnet** requires strict operational controls in addition to the automated script's pre-flight checks:
Expand Down
9 changes: 9 additions & 0 deletions k8s/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,12 @@ data:
FACILITATOR_URL: "http://backend:3000"
# Comma-separated browser origins allowed by CORS; set to your public host.
ALLOWED_ORIGINS: "https://app.example.com"
# Contract IDs deployed to the network above. These are managed in Git so
# `kubectl apply` and the running config stay in sync. Replace the
# placeholders with the IDs emitted by the contract deploy, then re-apply:
# kubectl apply -f k8s/configmap.yaml
# kubectl rollout restart deployment/backend
# (or use `scripts/rollback-contracts.cjs --configmap` to patch them).
ESCROW_CONTRACT_ID: ""
PAYMENT_CONTRACT_ID: ""
REGISTRY_CONTRACT_ID: ""
62 changes: 62 additions & 0 deletions k8s/examples/external-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ---------------------------------------------------------------------------
# ExternalSecret example for the backend-secret Secret.
#
# This manifest is an EXAMPLE ONLY and is intentionally NOT listed in
# k8s/kustomization.yaml, so `kubectl apply -k k8s/` never applies it.
#
# It requires the External Secrets Operator to be installed in the cluster
# (see docs/DEPLOY-KUBERNETES.md for setup steps). The operator syncs the
# referenced remote secret into the `backend-secret` Secret consumed by the
# backend deployment. The keys below match k8s/secrets.yaml.
#
# Adjust `secretStoreRef`, `remoteRef.key`, and the property names to match
# your secret backend (Vault, AWS Secrets Manager, GCP Secret Manager, etc.).
# ---------------------------------------------------------------------------
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: backend-secret
namespace: synapsvault
labels:
app.kubernetes.io/name: backend
app.kubernetes.io/component: secrets
spec:
# How often the operator re-syncs values from the remote store.
refreshInterval: 1h
secretStoreRef:
# Must reference an existing SecretStore/ClusterSecretStore.
name: backend-secret-store
kind: SecretStore
target:
# Sync into the same Secret the backend deployment consumes.
name: backend-secret
creationPolicy: Owner
data:
- secretKey: DATABASE_URL
remoteRef:
key: synapsvault/backend
property: DATABASE_URL
- secretKey: SUPABASE_SERVICE_KEY
remoteRef:
key: synapsvault/backend
property: SUPABASE_SERVICE_KEY
- secretKey: AGENT_SECRET_KEY
remoteRef:
key: synapsvault/backend
property: AGENT_SECRET_KEY
- secretKey: REGISTRY_SECRET_KEY
remoteRef:
key: synapsvault/backend
property: REGISTRY_SECRET_KEY
- secretKey: STELLAR_SECRET_KEY
remoteRef:
key: synapsvault/backend
property: STELLAR_SECRET_KEY
- secretKey: OPENROUTER_API_KEY
remoteRef:
key: synapsvault/backend
property: OPENROUTER_API_KEY
- secretKey: ADMIN_API_KEY
remoteRef:
key: synapsvault/backend
property: ADMIN_API_KEY
6 changes: 6 additions & 0 deletions k8s/ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ metadata:
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-connections: "20"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/hsts: "true"
nginx.ingress.kubernetes.io/hsts-max-age: "31536000"
nginx.ingress.kubernetes.io/hsts-include-subdomains: "true"
spec:
ingressClassName: nginx
tls:
Expand Down
Loading
Loading