# IT setup guide — Marcus-Law OutlookAddin

Audience: IT admin. Walks you through deploying the ClickOnce installer, distributing the code-signing certificate via GPO, provisioning per-user EspoCRM API keys, and operating the add-in for the 10 attorneys at Marcus-Law.

## Prerequisites

- Windows AD domain (`marcus-law.local`) with GPO push to all attorney machines.
- EspoCRM admin access on `https://crm.prod.marcus-law.co.il`.
- SSH access to `platform.dev.marcus-law.co.il` for serving the ClickOnce artifacts.
- Mattermost webhook for release notifications (`#git-verelasim`).
- Infisical project `outlook-addin` for storing secrets.

## 1. Build infrastructure

### Windows CI runner (one-time)

A Windows 11 Pro VM on Coolify hosts the Gitea Actions runner. It needs:

- **Visual Studio Build Tools 2022** with workloads:
  - `Microsoft.VisualStudio.Workload.Office` (for the VSTO MSBuild targets)
  - `Microsoft.VisualStudio.Workload.ManagedDesktop` (for WPF)
- **Windows SDK 10.0.22621** (for `signtool.exe`).
- **.NET Framework 4.8 Developer Pack** (`mage.exe`).
- **WSL Ubuntu** (for the `rsync` step of the publish job).
- **Gitea act_runner** registered as a Windows service with label `windows`.

### Code-signing certificate

The runner needs a code-signing certificate in `Cert:\CurrentUser\My`. Generate a self-signed cert once:

```powershell
$cert = New-SelfSignedCertificate `
  -Subject "CN=Marcus-Law OutlookAddin" `
  -Type CodeSigningCert `
  -CertStoreLocation Cert:\CurrentUser\My `
  -NotAfter (Get-Date).AddYears(3) `
  -KeyAlgorithm RSA `
  -KeyLength 2048
$thumbprint = $cert.Thumbprint
$pfxPassword = ConvertTo-SecureString -String "<generated-password>" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath C:\codesign.pfx -Password $pfxPassword
Export-Certificate  -Cert $cert -FilePath C:\codesign.cer   # public part — for GPO push
```

Store these in Infisical `/outlook-addin/`:

| Key | Value |
|---|---|
| `CODESIGN_THUMBPRINT` | The cert thumbprint (40 hex chars) |
| `CODESIGN_PFX_BASE64` | `[Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\codesign.pfx"))` |
| `CODESIGN_PASSWORD` | The PFX password |
| `PLATFORM_DEV_SSH_KEY` | Base64 of the rsync target's ed25519 private key |
| `MATTERMOST_WEBHOOK_RELEASES` | Incoming webhook URL for `#git-verelasim` |

In Gitea (`Settings → Actions → Secrets`), add four secrets that reference Infisical:

- `CODESIGN_THUMBPRINT`
- `PLATFORM_DEV_SSH_KEY`
- `MATTERMOST_WEBHOOK_RELEASES`

(The PFX itself is imported once on the runner; the workflow only uses the thumbprint.)

## 2. Deploy the certificate via GPO

The ClickOnce installer refuses to launch unless the publishing certificate is in **Trusted Publishers** on each attorney's machine. Use GPO to push it.

1. Copy `codesign.cer` to `\\marcus-law.local\SYSVOL\marcus-law.local\Scripts\certs\codesign.cer`.
2. Open **Group Policy Management Console** → edit a GPO scoped to the attorney OU.
3. Navigate to:
   `Computer Configuration → Policies → Windows Settings → Security Settings → Public Key Policies`
4. Right-click **Trusted Publishers → Import…** and pick `codesign.cer`.
5. Repeat under **Trusted Root Certification Authorities** (self-signed cert).
6. Force update everywhere:
   ```powershell
   Invoke-Command -ComputerName (Get-ADComputer -Filter * -SearchBase "OU=Attorneys,DC=marcus-law,DC=local").Name -ScriptBlock { gpupdate /force }
   ```
7. Verify on one attorney machine:
   ```powershell
   certutil -store -user TrustedPublisher | findstr /C:"Marcus-Law OutlookAddin"
   ```

## 3. nginx — serve the ClickOnce artifacts

On `platform.dev.marcus-law.co.il`, add `/etc/nginx/sites-available/outlook-addin.conf`:

```nginx
server {
    listen 443 ssl http2;
    server_name platform.dev.marcus-law.co.il;
    ssl_certificate     /etc/letsencrypt/live/platform.dev.marcus-law.co.il/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/platform.dev.marcus-law.co.il/privkey.pem;

    location /outlook-addin/ {
        alias /var/www/outlook-addin/;
        autoindex off;
        types {
            application/x-ms-application application;
            application/x-ms-manifest    manifest;
            application/octet-stream     vsto;
            application/octet-stream     deploy;
        }
        add_header Cache-Control "no-cache, must-revalidate";
    }
}
```

```bash
sudo mkdir -p /var/www/outlook-addin
sudo chown -R www-data:www-data /var/www/outlook-addin
sudo ln -s /etc/nginx/sites-available/outlook-addin.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```

The CI runner pushes new versions here on every `v*` tag via the publish job.

## 4. EspoCRM API key provisioning

Per attorney:

1. EspoCRM admin → **Administration → Users → Create**.
2. **User Type:** API User (or a Regular User with the "API" role).
3. **Username:** mail prefix (e.g. `chaim` for `chaim@marcus-law.co.il`).
4. **Role:** at least: read Contact / Case / Account / Lead / Email; create Email; read EmailAddress.
5. Save, then **Generate API Key**. Copy it.
6. Store under Infisical `/outlook-addin/users/<email>` (or send via a 1-time link service).

Send the attorney a personal email matching the ONBOARDING.md template, with their per-user key.

## 5. Release procedure (each new version)

1. Tag the commit on `main`:
   ```bash
   git tag v1.0.0
   git push origin v1.0.0
   ```
2. Gitea Actions builds Release, signs, generates the ClickOnce artifacts, rsyncs to platform.dev, and posts to `#git-verelasim`.
3. Smoke-test from a clean attorney machine (or a Windows 11 sandbox):
   - Browse to `https://platform.dev.marcus-law.co.il/outlook-addin/OutlookAddin.vsto`.
   - The ClickOnce installer should run with **no security warning**. If you see "Unknown Publisher", the GPO cert hasn't propagated to that machine.
   - First-run Settings → enter creds → Test Connection green.
   - Send a test email to a known contact, hit "תייק ל-EspoCRM", confirm it appears in the EspoCRM activity stream.
4. If a release goes badly, push a new tag with a fix; ClickOnce will roll all users forward automatically on their next Outlook start.

## 6. URL protocol handler (optional)

To make EspoCRM "Compose email" links open Outlook with the case pre-filled:

1. After the ClickOnce install, run as the user:
   ```powershell
   & "$env:LOCALAPPDATA\Apps\OutlookAddinProtocolHandler\install-protocol.ps1"
   ```
2. Verify: `start outlookaddin://compose?caseId=<id>` should open a new Outlook composer with the case pre-populated. (Outlook must be already running so the named pipe is up.)
3. To uninstall the registration: `uninstall-protocol.ps1`.

## 7. Monitoring

- **CI status** — Mattermost `#git-verelasim`, plus Gitea Actions UI.
- **Server-side filing** — EspoCRM internal logs (`data/logs/espo-*.log`) for `/api/v1/MailRouter/file` calls.
- **Client-side errors** — when an attorney reports an issue, ask them to send `%LOCALAPPDATA%\MarcusLaw\OutlookAddin\logs\addin-<date>.log`. Logs roll daily, retained 14 days.

## 8. Off-boarding (attorney leaves)

1. In EspoCRM admin, delete or disable the API User.
2. Their `creds.dat` on the laptop is DPAPI-encrypted with their Windows user profile — wiping the profile (or the laptop) is enough.
3. No central state to remove beyond EspoCRM.

## 9. Troubleshooting playbook

| Symptom | Likely cause | Fix |
|---|---|---|
| Install dialog says "Unknown Publisher" | GPO cert hasn't reached the machine | `gpupdate /force`, then `certutil -store -user TrustedPublisher` to confirm |
| Add-in installs but ribbon group is missing | `Microsoft.VSTORuntime.4.0` missing | ClickOnce normally pulls it; if Office < 2019, install [VSTO 4 Runtime](https://www.microsoft.com/download/details.aspx?id=48217) manually |
| Mass 401s after a renewal | API keys were rotated | Send new keys, ask users to re-open Settings and paste them |
| Retry queue is growing | EspoCRM unreachable, or 5xx errors | Check EspoCRM health; once it's back, the processor drains the queue on its 30-second loop |
| User says "categories disappeared" | They are local to that PC. If they reinstall Outlook on a new machine, categories don't sync (MAPI limitation) | Re-file is harmless — server-side dedups on `messageId` (409 returned, counted as success) |
