# Local Development ## Starting the dev server ```bash sinch functions dev ``` **Node.js** functions restart automatically when you save changes. **C#** functions use `dotnet watch run`. Default port is `3000`. Override with: ```bash sinch functions dev --port 8080 ``` ## Tunnel The tunnel proxies requests from the Sinch platform to your local machine. Without it, Sinch callbacks cannot reach `localhost`. On first run, the CLI asks for your tunnel preference. Your answer is saved in `sinch.json` under `tunnel.preference`. ```bash sinch functions dev --tunnel # Force tunnel on sinch functions dev --no-tunnel # Force tunnel off ``` When active, the tunnel registers a public endpoint and the platform automatically configures your Voice application's callback URL to point at it. ## Debugging ### Node.js ```bash sinch functions dev --debug ``` Starts the Node.js inspector on port `9229`. The CLI creates `.vscode/launch.json` automatically if it does not exist. - **VS Code / Cursor** — Press F5 to attach - **WebStorm** — Run > Attach to Node.js/Chrome > `localhost:9229` - **Manual** — connect to `ws://localhost:9229` ### C# ```bash sinch functions dev --debug ``` Creates `.vscode/launch.json` and `.vscode/tasks.json`. For Visual Studio: Debug > Attach to Process > `dotnet.exe`. For Rider: Run > Attach to Process. ### Debug environment variables | Variable | Effect | | --- | --- | | `DEBUG=1` | Verbose debug logging in CLI and runtime | | `DEBUG_HTTP=1` | Log all HTTP request and response details | ## Testing endpoints ```bash # ICE callback curl -s -X POST http://localhost:3000/ice \ -H "Content-Type: application/json" \ -d '{ "callid": "test-123", "cli": "+15551234567", "to": {"type": "number", "endpoint": "+15559876543"}, "domain": "pstn", "originationType": "pstn" }' # PIE callback (simulating DTMF press "1") curl -s -X POST http://localhost:3000/pie \ -H "Content-Type: application/json" \ -d '{ "callid": "test-123", "menuResult": { "menuId": "main", "value": "1", "inputMethod": "dtmf" } }' ``` ## Secrets Secrets are sensitive values stored in your OS keychain and injected at runtime. ### Pattern 1. Declare the key in `.env` (Node.js) or `appsettings.json` (C#) with an empty value 2. Store the actual value: `sinch secrets add OPENAI_API_KEY sk-proj-PLACEHOLDER` 3. The runtime loads values from the keychain at startup ### Use in code ```typescript const apiKey = process.env.OPENAI_API_KEY; ``` ### Manage secrets ```bash sinch secrets list # List key names (values hidden) sinch secrets get OPENAI_API_KEY # Show metadata, --show for value sinch secrets delete OPENAI_API_KEY # Remove from keychain ``` ### C# secrets `sinch functions init` runs `dotnet user-secrets init` automatically. To add manually: ```bash dotnet user-secrets set OPENAI_API_KEY sk-proj-PLACEHOLDER ``` ### On deploy The CLI reads your `.env` or `appsettings.json`. For keys with empty values, it looks up the actual value from the keychain. Secret variables are encrypted at rest and never appear in logs. ## Credential injection The dev server injects Sinch credentials as environment variables automatically: | Variable | Source | | --- | --- | | `SINCH_APPLICATION_KEY` | OS keychain (Voice application key) | | `SINCH_APPLICATION_SECRET` | OS keychain (Voice application secret) | ## What gets excluded from deployment `node_modules/`, `.env`, `.env.local`, `bin/`, `obj/`, `.git/`, `.vscode/`, `.idea/`, `.vs/`, `.cursor/`, `*.zip`, `*.user`, `*.suo`