# 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
```

If the port is already in use, `dev` fails immediately with an error instead of starting silently — free the port or choose another with `--port`.

## 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. `dev` warns you when this happens: real calls to that Voice application route to your machine until you stop dev — so use a dedicated development Voice app, not one handling production traffic.

## 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 |
|  --- | --- |
| `SINCH_DEBUG=1` | Verbose CLI debug logging (operations, timing) |
| `SINCH_DEBUG=2` | + HTTP requests (method, URL, status) |
| `SINCH_DEBUG=3` | + HTTP headers and response bodies (redacted) |


## 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`