diff --git a/docs/Account/01-intro.md b/docs/Account/01-intro.md new file mode 100644 index 0000000..f425d03 --- /dev/null +++ b/docs/Account/01-intro.md @@ -0,0 +1,28 @@ +--- +title: Account +sidebar: auto +sidebar_position: 1 +--- + +# Your TrustedLogin Account + +The TrustedLogin application at [app.trustedlogin.com](https://app.trustedlogin.com) handles account management, profiles, and billing. It receives and **processes login and validation requests** from the [Client SDK](../Client/intro) and [TrustedLogin Connector plugin](../Connector/intro). + +## Task guides {#guides} + +Step-by-step walkthroughs for the things you'll do most often in your TrustedLogin account: + +- **[Sign up and reach your first dashboard](./Guides/signup)** +- **[Install the Connector and connect your team](./Guides/install-connector)** +- **[Log in to a customer site](./Guides/log-in-to-site)** +- **[Invite a teammate](./Guides/invite-teammate)** · **[Switch between teams](./Guides/switch-team)** +- **[Buy credits](./Guides/buy-credits)** · **[Set up auto-reload](./Guides/auto-reload)** · **[Change plan or cancel](./Guides/change-plan)** +- **[Reset two-factor authentication](./Guides/reset-2fa)** · **[Manage your API keys](./Guides/regenerate-api-keys)** + +See [the full list of guides](./Guides) for the same content grouped by topic. + +## SLA {#service-level-agreement} + +We have a 99.99% uptime commitment for our Enterprise-level customers. Please [read the SLA on our website](https://www.trustedlogin.com/service-level-agreement/). + +If you're building automation or custom integrations on top of TrustedLogin, the [HTTP API reference](../api-reference) covers authentication, endpoints, and a Postman collection. diff --git a/docs/Account/Developers/_category_.json b/docs/Account/Developers/_category_.json new file mode 100644 index 0000000..262c166 --- /dev/null +++ b/docs/Account/Developers/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Developers", + "position": 9, + "collapsible": true, + "collapsed": true +} diff --git a/docs/Account/Developers/http-api.md b/docs/Account/Developers/http-api.md new file mode 100644 index 0000000..bd78cdb --- /dev/null +++ b/docs/Account/Developers/http-api.md @@ -0,0 +1,18 @@ +--- +sidebar_label: HTTP API +sidebar_position: 5 +sidebar: Home +--- + +# HTTP API + +The TrustedLogin HTTP API is the programmatic interface to the same operations the dashboard exposes — useful for building custom integrations, automation, or your own tooling on top of TrustedLogin. + +Full reference is hosted alongside the application: + +- [Authenticating requests](https://app.trustedlogin.com/docs/api/#authenticating-requests) +- [Accounts API](https://app.trustedlogin.com/docs/api/#accounts-api) +- [Endpoints](https://app.trustedlogin.com/docs/api/#endpoints) +- [Sites API](https://app.trustedlogin.com/docs/api/#sites-api) + +A [Postman collection](https://app.trustedlogin.com/docs/collection.json) is also available. diff --git a/docs/Account/Guides/_category_.json b/docs/Account/Guides/_category_.json new file mode 100644 index 0000000..043d2e2 --- /dev/null +++ b/docs/Account/Guides/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Guides", + "position": 2, + "collapsible": true, + "collapsed": false +} diff --git a/docs/Guides/auto-reload.md b/docs/Account/Guides/auto-reload.md similarity index 91% rename from docs/Guides/auto-reload.md rename to docs/Account/Guides/auto-reload.md index 9bed1f9..9e29fcf 100644 --- a/docs/Guides/auto-reload.md +++ b/docs/Account/Guides/auto-reload.md @@ -3,7 +3,7 @@ sidebar_label: Set up auto-reload sidebar_position: 7 --- - + # Set up auto-reload @@ -33,7 +33,7 @@ There are three settings that together shape when and how much auto-reload costs - **Reload when my balance drops below** — the trigger. When your credit balance slips under this number, auto-reload fires. A common setting is $5 — enough headroom to cover a few more logins while the card charge settles. - **Reload with** — the bundle that gets purchased each time. Bigger bundles cost more per reload but carry a bonus (a $100 bundle gives you $115 in credits) and charge you less often. -- **Monthly cap** — the most auto-reload will spend on your card in any one billing cycle, no matter what. If a customer ever went rogue and burned through credit in a loop, this is the ceiling that stops the bleeding. +- **Spend cap per billing period** — the most auto-reload will spend on your card in any one cycle, no matter what. If a customer ever went rogue and burned through credit in a loop, this is the ceiling that stops the bleeding. Click **Save** and auto-reload is live. @@ -69,5 +69,4 @@ Teams doing heavier support work should go with a larger bundle (the bonus credi ## If something goes wrong -A failed charge pauses auto-reload until you fix the payment method. You'll get an email at your billing address, and the banner at the top of the Billing page shows what went wrong. Fixing the card and saving your billing settings resumes auto-reload on the next trigger. - +A failed charge pauses auto-reload until you fix the payment method. You'll get an email at your billing address, and the banner at the top of the Billing page shows what went wrong. Fixing the card and saving your billing settings resumes auto-reload on the next trigger. \ No newline at end of file diff --git a/docs/Guides/buy-credits.md b/docs/Account/Guides/buy-credits.md similarity index 61% rename from docs/Guides/buy-credits.md rename to docs/Account/Guides/buy-credits.md index 1309127..f88e60a 100644 --- a/docs/Guides/buy-credits.md +++ b/docs/Account/Guides/buy-credits.md @@ -3,7 +3,7 @@ sidebar_label: Buy credits sidebar_position: 6 --- - + # Buy credits @@ -11,7 +11,7 @@ One-time credit purchases — useful when a specific week needs extra headroom a ## 1. Open your Billing page -Click **Billing** in the sidebar. Your current credit balance shows at the top of the page; the bundle grid sits below the plan card. +Click **Billing** in the sidebar. Your current credit balance shows at the top of the page; the bundle grid sits one click away on the **Buy credits** page. ![Open your Billing page](/img/guides/buy-credits/01-open-your-billing-page.png) @@ -19,32 +19,23 @@ Click **Billing** in the sidebar. Your current credit balance shows at the top o ## 2. Pick a bundle -There are six bundle sizes, from $10 (covers the occasional overage) through $1,000 (Commit-tier teams who want to pay up front). Bigger bundles get bonus credits: - -- **Starter** ($10) — 10 credits, no bonus. Good for topping up after a one-off spike. -- **Standard** ($25) — 25 credits, no bonus. The recommended default. -- **Plus** ($50) — 55 credits (+10% bonus). -- **Growth** ($100) — 115 credits (+15% bonus). -- **Scale** ($250) — 300 credits (+20% bonus). -- **Commit** ($1,000) — 1,250 credits (+25% bonus). - -The bigger the bundle, the better the value, and credits never expire while your team is active — so there's no rush to use them. +Six bundle sizes, from **$10** (small top-up) to **$1,000** (Commit teams paying up front). Bundles of $50+ add bonus credits on top of the paid amount, scaling up to **+25%** on the largest bundle. Credits never expire while your team is active — pick the size that covers a few weeks at your team's typical burn. ![Pick a bundle](/img/guides/buy-credits/02-pick-a-bundle.png) -## 3. Watch for the overage callout +## 3. Bigger bundles carry bonus credits -If you're currently over your plan's login cap, a yellow banner appears above the bundle grid: **You're N logins over your cap this period.** The number tells you exactly which bundle covers the overage with some headroom — usually the Standard ($25) bundle does the job. +Each card shows the bundle size, the credits it buys, and — for $50 and up — a **+10%** through **+25%** bonus badge. Bonus credits are real balance, not a future discount. -![Watch for the overage callout](/img/guides/buy-credits/03-watch-for-the-overage-callout.png) +![Bigger bundles carry bonus credits](/img/guides/buy-credits/03-bigger-bundles-carry-bonus-credits.png) ## 4. Buy for $X → Stripe Checkout -Clicking **Buy for $X** on any bundle card opens Stripe Checkout in the same tab. Use the card on file (or enter a new one) and confirm — Stripe handles the charge, we handle the credit. +Clicking **Buy for $X** on any bundle card opens Stripe Checkout. Use the card on file (or enter a new one) and confirm — Stripe handles the charge, we handle the credit. On a successful payment you bounce back to TrustedLogin with a **Credits on the way** toast. Your balance updates within a few seconds of the Stripe webhook arriving. If you cancel Stripe Checkout instead, you're returned to the same page with a **Checkout canceled** toast and no charge. @@ -68,5 +59,4 @@ Count your team's typical weekly login burn and multiply by four to six weeks. T ## Refund policy -Credit bundles are refundable inside 14 days as long as the credits haven't been spent. Contact support to request a refund. - +Credit bundles are refundable inside 14 days as long as the credits haven't been spent. Contact support to request a refund. \ No newline at end of file diff --git a/docs/Guides/change-plan.md b/docs/Account/Guides/change-plan.md similarity index 52% rename from docs/Guides/change-plan.md rename to docs/Account/Guides/change-plan.md index 60b433b..85c6adc 100644 --- a/docs/Guides/change-plan.md +++ b/docs/Account/Guides/change-plan.md @@ -3,7 +3,7 @@ sidebar_label: Change plan or cancel sidebar_position: 8 --- - + # Change plan or cancel @@ -11,7 +11,7 @@ Plan changes — up, down, or off — all flow through Stripe's Billing Portal. ## 1. Open your Billing page -**Billing** in the sidebar takes you to the plan summary. The top card shows your current plan, next renewal date, and billing email. Every plan change starts from here. +**Billing** in the sidebar takes you to the plan summary. The top card shows your current plan, balance, and the plan picker grid. Every plan change starts from here. ![Open your Billing page](/img/guides/change-plan/01-open-your-billing-page.png) @@ -21,35 +21,31 @@ Plan changes — up, down, or off — all flow through Stripe's Billing Portal. Before changing anything, check what you're on: -- **Plan name** — Free, Starter, Growth, Scale, and Enterprise are the common ones. -- **Sites** and **logins per cycle** — the caps. Any login beyond this is billed from your credit balance (so running out of credits = hitting a hard stop, not overage charges). -- **Next invoice** — the date your card will be charged for the next cycle. Changes you make before this date take effect on the next cycle; mid-cycle changes prorate. +- **Plan name** — Free, Solo, Team, Agency, and Enterprise are the public tiers. +- **Sites** and **logins per cycle** — the caps. Any login beyond this is billed from your credit balance (running out of credits = a hard stop, not overage charges). +- **Credit balance** — credits you have on hand right now. Refill via auto-reload or the Buy credits page. ![Understand what your current plan covers](/img/guides/change-plan/02-understand-what-your-current-plan-covers.png) -## 3. Click Manage subscription +## 3. Click Billing portal to manage your subscription -The **Manage subscription** button opens the Stripe Billing Portal. Stripe handles the plan change itself — we don't build our own checkout for it, because letting Stripe own the card data, the tax calculation, and the invoices means one less system that can leak them. +The **Billing portal** button opens the Stripe Billing Portal in a new tab. Stripe handles the plan change itself — we don't build our own checkout for it, because letting Stripe own the card data, the tax calculation, and the invoices means one less system that can leak them. -You're signed in automatically — no extra password to enter. Stripe's portal is a different tab; when you're done, close it and your plan is already updated on our side through Stripe webhooks. +You're signed in automatically — no extra password to enter. When you're done, close the Stripe tab and your plan is already updated on our side through Stripe webhooks. -![Click Manage subscription](/img/guides/change-plan/03-click-manage-subscription.png) +![Click Billing portal to manage your subscription](/img/guides/change-plan/03-click-billing-portal-to-manage-your-subscription.png) -## 4. Change plan, update card, or cancel +## 4. Or switch plans in the picker -The Stripe portal has three relevant actions: +Below the plan summary, every public tier is listed as a card with its sites / logins caps, monthly price, and overage rates. Click **Upgrade to Team / Agency** to switch up, **Switch to Free** to downgrade. Upgrades go through Stripe Checkout if you don't have a card on file yet, OR swap your existing subscription's price in place if you do — either way, you never end up paying two subscriptions concurrently. -**Change plan.** Picks a new plan from the list — effective immediately, prorated against the current cycle. If you're upgrading, the difference is charged now; if you're downgrading, the difference becomes credit that offsets your next invoice. +Mid-cycle changes prorate: upgrades charge the per-day difference now, downgrades credit the difference toward your next invoice. -**Update payment method.** Add or replace the card on file. The update applies to every future charge, including auto-reload. - -**Cancel subscription.** Ends the subscription at the end of the current cycle — you keep all the features you're paying for until the date your card would have been charged next. After that, you drop to the Free plan. - -![Change plan, update card, or cancel](/img/guides/change-plan/04-change-plan-update-card-or-cancel.png) +![Or switch plans in the picker](/img/guides/change-plan/04-or-switch-plans-in-the-picker.png) @@ -69,5 +65,4 @@ Credits only disappear if you **delete your team**. Cancelling a subscription do **Do I lose access immediately when I cancel?** No. Cancel is an end-of-period action. You keep every feature of the plan you were on until the cycle closes — so cancelling two days into a monthly plan still gives you 28 more days of paid access. -**Can I get a refund instead of a credit?** For annual plans or edge cases (you signed up by mistake, you were double-charged), yes — contact support with your account email and we'll handle it manually. - +**Can I get a refund instead of a credit?** For annual plans or edge cases (you signed up by mistake, you were double-charged), yes — contact support with your account email and we'll handle it manually. \ No newline at end of file diff --git a/docs/Guides/index.md b/docs/Account/Guides/index.md similarity index 74% rename from docs/Guides/index.md rename to docs/Account/Guides/index.md index f4aa3e7..f597b3c 100644 --- a/docs/Guides/index.md +++ b/docs/Account/Guides/index.md @@ -1,12 +1,7 @@ --- +title: Task guides sidebar_label: Overview sidebar_position: 0 -slug: /Guides ---- - ---- -title: Task guides -sidebar_label: Task guides --- # Common things you'll do in TrustedLogin @@ -39,12 +34,3 @@ them. - [Reset two-factor authentication](reset-2fa.md) - [Regenerate your API keys](regenerate-api-keys.md) - ---- - - diff --git a/docs/Account/Guides/install-connector.md b/docs/Account/Guides/install-connector.md new file mode 100644 index 0000000..afaac0f --- /dev/null +++ b/docs/Account/Guides/install-connector.md @@ -0,0 +1,46 @@ +--- +sidebar_label: Install the Connector +sidebar_position: 2 +--- + + + +# Install the Connector and connect your team + +The Connector is a WordPress plugin you install on **your** site — not your customers' sites. It's the bridge between TrustedLogin and the WordPress installations your support team works from. + +This guide covers the TrustedLogin side of the setup. The Connector plugin itself has [its own setup guide](../../Connector/01-intro.md) for installation and configuration. + +## 1. Open your team settings on TrustedLogin + +Your dashboard lives at **trustedlogin.com/admin**. Once you're signed in, open **Team Settings** from the sidebar. Everything you'll paste into the Connector lives on this page. + +![Open your team settings on TrustedLogin](/img/guides/install-connector/01-open-your-team-settings-on-trustedlogin.png) + + + +## 2. Fill in your team identity + +The **Team name** appears on your customers' screens when they grant you access — pick something they'll recognize (usually your product's name). + +The **REST API endpoint** is the URL of the WordPress site where you'll run the Connector plugin, with `/wp-json/` on the end — so if your Connector lives at `https://support.example.com`, enter `https://support.example.com/wp-json/`. + +The **Support URL** is where customers will land if they need help after a grant expires. A good choice is your product's help desk or contact page. + +![Fill in your team identity](/img/guides/install-connector/02-fill-in-your-team-identity.png) + + + +## 3. Copy your API credentials + +Scroll to the **API credentials** section. You need three values to paste into the Connector: + +- **Account ID** — a short number that identifies your team. +- **Public Key** — a 16-character hex string. +- **Private Key** — another 16-character hex string. **Keep this one private** — anyone who has it can impersonate your team until you rotate it. + +Each value has a **Copy** button next to it. Leave this tab open — you'll need them in the next step. + +![Copy your API credentials](/img/guides/install-connector/03-copy-your-api-credentials.png) + + \ No newline at end of file diff --git a/docs/Guides/invite-teammate.md b/docs/Account/Guides/invite-teammate.md similarity index 94% rename from docs/Guides/invite-teammate.md rename to docs/Account/Guides/invite-teammate.md index a4e7a94..bfa0374 100644 --- a/docs/Guides/invite-teammate.md +++ b/docs/Account/Guides/invite-teammate.md @@ -3,7 +3,7 @@ sidebar_label: Invite a teammate sidebar_position: 4 --- - + # Invite a teammate @@ -71,5 +71,4 @@ Email deliverability is the single most common issue here. Check: 1. **Spam / promotions folders.** TrustedLogin invites come from a `trustedlogin.com` address but spam filters can still flag them. 2. **The Invitations page.** Is the invite still listed as **Pending**? If yes, TrustedLogin sent it — the issue is on the receiving mailbox. Resend from there. -3. **Typos in the email address.** Revoke the bad invite and start over with the correct address. - +3. **Typos in the email address.** Revoke the bad invite and start over with the correct address. \ No newline at end of file diff --git a/docs/Account/Guides/log-in-to-site.md b/docs/Account/Guides/log-in-to-site.md new file mode 100644 index 0000000..921a4e8 --- /dev/null +++ b/docs/Account/Guides/log-in-to-site.md @@ -0,0 +1,78 @@ +--- +sidebar_label: Log in to a customer site +sidebar_position: 3 +--- + + + +# Log in to a customer site + +Logging into a customer's WordPress site is the reason TrustedLogin exists. Once a customer has granted you access from their side (see [the Client SDK guide](../../Client/01-intro.md) for how that button appears on their end), the grant shows up on your Sites list and you're one click away from being inside their admin. + +## 1. Open the Sites list + +Click **Sites** in the sidebar. Every WordPress site one of your customers has granted you access to shows up here — one row per site, newest grants at the top. + +![Open the Sites list](/img/guides/log-in-to-site/01-open-the-sites-list.png) + + + +## 2. Find the site you want to log into + +Each row shows the site URL, the support role the customer granted, and when the grant expires. Grants have a lifetime (usually a week) so you don't end up with permanent keys to every customer site you've ever helped — if the row says **Expired**, you'll need to ask the customer for a fresh grant before you can log in. + +Use the search box or filters at the top to narrow down to the site you're looking for. The column on the far right is a one-click **Edit** action that opens the full record + Log in affordance. + +![Find the site you want to log into](/img/guides/log-in-to-site/02-find-the-site-you-want-to-log-into.png) + + + +## 3. Click Edit to open the site + +Clicking **Edit** opens the site's record in the next screen. From there, the **Log in to site** button opens the customer's WordPress admin in a new tab — already signed in, as a temporary user with the role the customer granted. You don't need to know the customer's password. You don't need them to be online. + +When the grant is expired the Log in button is disabled — you'll need a fresh grant first. + +![Click Edit to open the site](/img/guides/log-in-to-site/03-click-edit-to-open-the-site.png) + + + +## 4. See the full site record + +Click **Edit** on a row to see the full site record. This page shows the access key, endpoint URL, who on the customer side granted the access, and every log-in to this particular site. + +The **Log in to site** button at the top does the same thing as the row affordance — opens the customer site through the Connector. The **Copy** button next to the access key is audit-logged, so you have a paper trail any time a credential leaves this screen. + +![See the full site record](/img/guides/log-in-to-site/04-see-the-full-site-record.png) + + + +## 5. Click Log in to site + +The blue **Log in to site** button at the top of the page opens the customer's WordPress admin in a new tab. TrustedLogin signs you in as a temporary user with the role the customer granted — no password to enter, no request to the customer to be online. + +When you arrive in the customer admin, your activity is logged on both sides: TrustedLogin records who logged in and when; the Connector plugin records the same on the customer's site. Close the tab when you're done. + +![Click Log in to site](/img/guides/log-in-to-site/05-click-log-in-to-site.png) + + + +## 6. Revoke access if something feels wrong + +Grants expire on their own, so you rarely need to revoke them manually. When you do — a customer asks, a teammate leaves, something on the access trail feels wrong — scroll to the **Danger zone** at the bottom of the site's edit page and click **Revoke access**. The access key stops working immediately, and the action is irreversible: the customer needs to grant a fresh key for any future logins to that site. + +![Revoke access if something feels wrong](/img/guides/log-in-to-site/06-revoke-access-if-something-feels-wrong.png) + + + +## Logging in from the customer's WordPress admin + +There is a second way in: the **Connector plugin** on the customer's WordPress site exposes the same grants under **TrustedLogin** in the WordPress admin sidebar. Site admins (and anyone the customer has authorized) can click **Login as support** there to land in the same signed-in session, without ever visiting the TrustedLogin dashboard. + +Use the dashboard path when you're chasing a ticket and know which customer it is; use the Connector path when you're already on the customer's site (e.g. they've shared a screen and want you to take over). + +## What happens if a login doesn't work + +The first thing to check is whether the grant is still active. If the row says **Expired**, the customer needs to generate a new grant — the old one isn't recoverable. + +If the grant shows as active but the Log in button takes you to a WordPress login form instead of landing you signed in, something on the customer's end is wrong: the Connector plugin is likely inactive or the identifier TrustedLogin uses to route the login doesn't match. [The Connector troubleshooting guide](../../Connector/troubleshooting.md) covers the common causes. \ No newline at end of file diff --git a/docs/Account/Guides/regenerate-api-keys.md b/docs/Account/Guides/regenerate-api-keys.md new file mode 100644 index 0000000..f081ce0 --- /dev/null +++ b/docs/Account/Guides/regenerate-api-keys.md @@ -0,0 +1,72 @@ +--- +sidebar_label: Manage API keys +sidebar_position: 10 +--- + + + +# Manage your API keys + +Your TrustedLogin **Team ID**, **API Key**, and **Private Key** are the three values the Connector plugin needs to talk back to TrustedLogin. This guide walks through finding them, sharing them safely with a developer, and what to do if a key has leaked. + +Self-serve **regeneration** of the keys via the dashboard isn't shipped yet — if you need a true rotation (suspected leak, departing employee, periodic hygiene), email support (support@trustedlogin.com) and a member of the team will cycle the keys via the management API. In the meantime, **Pause team** is a fast self-serve circuit-breaker that blocks every login until you unpause. + +## 1. Open Team Settings + +Your API credentials live on the **Team Settings** page in the **API credentials** section. Only team **owners** and **admins** see the Private Key — members of the team see the rest of the section but the sensitive value is never loaded into their browser. + +![Open Team Settings](/img/guides/regenerate-api-keys/01-open-team-settings.png) + + + +## 2. Find the API credentials section + +Three values live here: **Team ID**, **API Key**, and **Private Key**. Team ID and API Key are public identifiers — fine to drop into a support ticket or a config file. Private Key is the secret half — treat it like a password. + +![Find the API credentials section](/img/guides/regenerate-api-keys/02-find-the-api-credentials-section.png) + + + +## 3. Reveal the Private Key only when you need to copy it + +**Click Show** to reveal the Private Key, **Copy** to put it on your clipboard. Paste it into your Connector plugin's settings page in WordPress and save. + +TrustedLogin never displays the Private Key again automatically — every reveal is an explicit Show click. If you ever forget where you saved it, this page is the only place to retrieve it. + +![Reveal the Private Key only when you need to copy it](/img/guides/regenerate-api-keys/03-reveal-the-private-key-only-when-you-need-to-copy-it.png) + + + +## 4. Email the setup instructions to your developer in one click + +Most non-technical owners land here with no idea what to do with these values, so the **Email to my developer** button drops a pre-filled mailto with the Team ID, API Key, the plugin URL, and instructions to log in here and grab the Private Key themselves. + +The Private Key is **not** included in the email — a one-click email primitive shouldn't exfiltrate the same value the help text on this page calls out as "sensitive." + +![Email the setup instructions to your developer in one click](/img/guides/regenerate-api-keys/04-email-the-setup-instructions-to-your-developer-in-one-click.png) + + + +## 5. If something feels wrong, pause or delete the team + +If a key has leaked and you don't have time to chase down every Connector site that's using it, the **Pause team** button blocks **all** support logins immediately without revoking the keys themselves. Pause is fast and reversible — **Unpause** the team when you're back in a safe state. + +For a true emergency (the team should never log in again), **Delete team** in the Danger zone is the irreversible path: keys, sites, slug all released. You'll need to create a fresh team and re-onboard your Connector sites from scratch. + +![If something feels wrong, pause or delete the team](/img/guides/regenerate-api-keys/05-if-something-feels-wrong-pause-or-delete-the-team.png) + + + +## When to ask for a rotation + +- **Suspected exposure.** A key appeared in a log file, a screen-share recording, a Slack message, a public repo, an error report sent to a vendor — any time a key has touched a surface it shouldn't have. +- **A teammate left.** If that teammate had access to your Connector plugin settings. +- **Periodic hygiene.** Some teams rotate quarterly or after each major release, just as a matter of policy. + +In all three cases, **Pause team** first (fast, reversible) and then email support to coordinate the actual key cycle. + +## What rotation does NOT affect + +Your existing **access grants** from customers are not invalidated by a key rotation. Each grant is scoped to an individual customer site, with its own identifier and expiration — they keep working while the fresh Public and Private Keys flow through the Connector for new grants. + +**Team ID** stays the same. Only the API Key and Private Key rotate. \ No newline at end of file diff --git a/docs/Guides/reset-2fa.md b/docs/Account/Guides/reset-2fa.md similarity index 89% rename from docs/Guides/reset-2fa.md rename to docs/Account/Guides/reset-2fa.md index e922be2..677935f 100644 --- a/docs/Guides/reset-2fa.md +++ b/docs/Account/Guides/reset-2fa.md @@ -3,7 +3,7 @@ sidebar_label: Reset 2FA sidebar_position: 9 --- - + # Reset two-factor authentication @@ -51,7 +51,7 @@ If your phone is gone and you can't get a code anymore, you're locked out of you To request a reset: -1. Email **[email protected]** from the address on your TrustedLogin account. We cross-check the address before acting — if you email from somewhere else we may ask you to prove you own the account. +1. Email **support@trustedlogin.com** from the address on your TrustedLogin account. We cross-check the address before acting — if you email from somewhere else we may ask you to prove you own the account. 2. Include your account email and the name of your team. 3. Be ready for a short identity-verification step — either a reply from the address on file or a quick video call, depending on the account's sensitivity. @@ -59,5 +59,4 @@ Once we reset your 2FA, the next time you sign in TrustedLogin will walk you str ## Why you can't turn 2FA off -TrustedLogin accounts carry keys that let your team sign into customer sites. A stolen password that could sign in directly would be a critical incident; 2FA is the single biggest defense against that scenario. Making it optional would shift that risk to every TrustedLogin user whether they wanted it or not. So: 2FA stays on for everyone, always. - +TrustedLogin accounts carry keys that let your team sign into customer sites. A stolen password that could sign in directly would be a critical incident; 2FA is the single biggest defense against that scenario. Making it optional would shift that risk to every TrustedLogin user whether they wanted it or not. So: 2FA stays on for everyone, always. \ No newline at end of file diff --git a/docs/Guides/signup.md b/docs/Account/Guides/signup.md similarity index 57% rename from docs/Guides/signup.md rename to docs/Account/Guides/signup.md index 20de224..9559683 100644 --- a/docs/Guides/signup.md +++ b/docs/Account/Guides/signup.md @@ -3,11 +3,11 @@ sidebar_label: Sign up sidebar_position: 1 --- - + # Getting started: your first sign-up -This walkthrough covers everything from creating your TrustedLogin account through seeing your first dashboard and turning on two-factor authentication. If you get stuck at any step, check [the Getting Started guide](../getting-started.md) or reach out through the support link in your account. +This walkthrough covers everything from creating your TrustedLogin account through seeing your first dashboard and turning on two-factor authentication. If you get stuck at any step, check [the Getting Started guide](../../getting-started.md) or reach out through the support link in your account. ## 1. Create your account @@ -21,7 +21,7 @@ Passwords need to be at least 16 characters. A long passphrase — something lik ## 2. Review your details and register -Double-check your email address before clicking **Register** — it's harder to change later than to get right the first time. Click Register when you're ready. +Double-check your email address before clicking **Create account** — it's harder to change later than to get right the first time. Click Create account when you're ready. ![Review your details and register](/img/guides/signup/02-review-your-details-and-register.png) @@ -37,35 +37,33 @@ Didn't get the email? Check spam, then use **Resend verification email** to send -## 4. Welcome to your dashboard +## 4. Enable two-factor authentication -This is your team's home. We've already created a personal team for you (named after you) on our free plan — 3 connected sites and 10 support logins per billing cycle, no credit card needed. +TrustedLogin requires two-factor authentication on every account, so the moment your email is verified you'll land here. Scan the QR code with any authenticator app — 1Password, Authy, Google Authenticator, Microsoft Authenticator all work the same way. Your app will start showing a six-digit code that changes every 30 seconds. -You don't have any sites yet, so the **Recent support access** card is empty. Your next step is to install the TrustedLogin Connector plugin on a WordPress site — the link in that card walks you through it. - -![Welcome to your dashboard](/img/guides/signup/04-welcome-to-your-dashboard.png) +Enter the current code from your app into the **Token** field and click **Enable 2FA**. From then on, you'll enter a code from your authenticator app every time you sign in. - +![Enable two-factor authentication](/img/guides/signup/04-enable-two-factor-authentication.png) -## 5. Set up your profile + -Click your avatar or the **User settings** link to update your name, email, or password. Two-factor authentication (2FA) is also enabled here. +## 5. Welcome to your dashboard -2FA is a second step when you sign in — a six-digit code from an authenticator app — so even if someone gets your password they can't get into your account. **2FA is required on TrustedLogin; you can't turn it off once you enable it.** +Once 2FA is set up, this is your team's home. We've already created a personal team for you (named after you) on our free plan — 3 connected sites and 10 support logins per billing cycle, no credit card needed. -![Set up your profile](/img/guides/signup/05-set-up-your-profile.png) +You don't have any sites yet, so the **Recent support access** card is empty. Your next step is to install the TrustedLogin Connector plugin on a WordPress site — the link in that card walks you through it. - +![Welcome to your dashboard](/img/guides/signup/05-welcome-to-your-dashboard.png) -## 6. Enable two-factor authentication + -Scan the QR code with any authenticator app — 1Password, Authy, Google Authenticator, Microsoft Authenticator all work the same way. Your app will start showing a six-digit code that changes every 30 seconds. +## 6. Set up your profile -Enter the current code from your app into the **Token** field and click **Enable 2FA**. That's it — from now on, you'll enter a code from your authenticator app every time you sign in. +Click your avatar or the **User settings** link to update your name, email, or password whenever you need to. This is also where you regenerate 2FA recovery codes if you ever lose the device with your authenticator app — keep them somewhere safe, because losing both is the only thing that can lock you out of your own account. -![Enable two-factor authentication](/img/guides/signup/06-enable-two-factor-authentication.png) +![Set up your profile](/img/guides/signup/06-set-up-your-profile.png) - + ## 7. See your plan and buy extra credits @@ -79,7 +77,6 @@ If you'd rather not think about topping up, scroll to the **auto-reload** sectio ## What to do next -1. **[Install the Connector plugin](../Connector/01-intro.md)** on the WordPress site where you'll receive support requests. -2. **[Add TrustedLogin to your plugin or theme](../Client/01-intro.md)** so your customers can grant you secure access. -3. **[Review our security model](../security.md)** to understand how we protect credentials in transit and at rest. - +1. **[Install the Connector plugin](../../Connector/01-intro.md)** on the WordPress site where you'll receive support requests. +2. **[Add TrustedLogin to your plugin or theme](../../Client/01-intro.md)** so your customers can grant you secure access. +3. **[Review our security model](../../security.md)** to understand how we protect credentials in transit and at rest. \ No newline at end of file diff --git a/docs/Guides/switch-team.md b/docs/Account/Guides/switch-team.md similarity index 89% rename from docs/Guides/switch-team.md rename to docs/Account/Guides/switch-team.md index f65903d..9237dc9 100644 --- a/docs/Guides/switch-team.md +++ b/docs/Account/Guides/switch-team.md @@ -43,7 +43,7 @@ The dashboard reloads on the team you picked. The team name in the header is the ## 5. Or press ⌘K to jump -For the keyboard-first folks: press **⌘K** (Ctrl+K on Windows/Linux) from anywhere to open the command palette. Start typing and the palette filters live — pages, settings, your teams, and (for super-admins) customer teams all appear in one list. Pick an entry and press Enter to jump. +For the keyboard-first folks: press ⌘K (Ctrl+K on Windows/Linux) from anywhere to open the command palette. Start typing and the palette filters live — pages, settings, your teams, and (for super-admins) customer teams all appear in one list. Pick an entry and press Enter to jump. ![Or press ⌘K to jump](/img/guides/switch-team/05-or-press-k-to-jump.png) @@ -55,5 +55,4 @@ Most TrustedLogin accounts only need one team — one product, one set of custom - Support **two separate products** with different branding. Each team has its own name, REST endpoint, and plan, so a customer granting access sees the right name. - Run a **client services business** where each client gets their own team name and branding. Each client team can have its own billing — useful if clients pay their own subscription costs. -- Want **separation of duties** between a staging and production support operation, though most teams just use different Connector installs for that. - +- Want **separation of duties** between a staging and production support operation, though most teams just use different Connector installs for that. \ No newline at end of file diff --git a/docs/SaaS/data-storage.md b/docs/Account/data-storage.md similarity index 100% rename from docs/SaaS/data-storage.md rename to docs/Account/data-storage.md diff --git a/docs/SaaS/subcontractors.md b/docs/Account/subcontractors.md similarity index 100% rename from docs/SaaS/subcontractors.md rename to docs/Account/subcontractors.md diff --git a/docs/Client/01-intro.md b/docs/Client/01-intro.md index f248bfe..d56c731 100644 --- a/docs/Client/01-intro.md +++ b/docs/Client/01-intro.md @@ -8,7 +8,7 @@ sidebar_position: 1 Easily and securely log in to your customers sites when providing support. -## Our priority: Be secure and [don't crash sites](https://www.bugsnag.com/blog/sdks-should-not-crash-apps) {#our-priority-sdks-should-not-crash-sites} +## Our priority: Be secure and don't crash sites {#our-priority-sdks-should-not-crash-sites} When you integrate TrustedLogin into your project (theme, plugin, or custom code), you are counting on us not to mess up your customer or clients' sites. We take that extremely seriously. diff --git a/docs/Client/namespacing/css-namespacing.md b/docs/Client/namespacing/css-namespacing.md index b8dc801..3ff0c80 100644 --- a/docs/Client/namespacing/css-namespacing.md +++ b/docs/Client/namespacing/css-namespacing.md @@ -13,7 +13,7 @@ The `build-sass` script accepts the following arguments: - `--assets_dir`: The path to the TrustedLogin client directory, used locate the SCSS source files. Optional. Default: `(vendor-namespaced|vendor-prefixed)/trustedlogin/client/src/assets/`. - `--export_dir`: The path to the output directory where the generated CSS will be saved. Optional. Default: to `(vendor-namespaced|vendor-prefixed)/trustedlogin/client/src/assets/`. -The default way to namespace files is [as a Composer script](/Client/01-intro.md), but this may not work with your build process: the default implementation shown adds the required SCSS package (`scssphp/scssphp`) to the `require-dev` array, which may not work with your release flow. If you move `scssphp/scssphp` to the `require` array, the scssphp library will be included in your autoloader, which adds bloat for something that should be used one-time. +The default way to namespace files is [as a Composer script](/Client/intro), but this may not work with your build process: the default implementation shown adds the required SCSS package (`scssphp/scssphp`) to the `require-dev` array, which may not work with your release flow. If you move `scssphp/scssphp` to the `require` array, the scssphp library will be included in your autoloader, which adds bloat for something that should be used one-time. :::info ### When you see `ProBlockBuilder`, make sure to replace with your own namespace! {#when-you-see-problockbuilder-make-sure-to-replace-with-your-own-namespace} diff --git a/docs/Connector/Developers/_category_.json b/docs/Connector/Developers/_category_.json new file mode 100644 index 0000000..262c166 --- /dev/null +++ b/docs/Connector/Developers/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Developers", + "position": 9, + "collapsible": true, + "collapsed": true +} diff --git a/docs/Connector/development.md b/docs/Connector/Developers/development.md similarity index 100% rename from docs/Connector/development.md rename to docs/Connector/Developers/development.md diff --git a/docs/Connector/Developers/envelope-signature-verification.md b/docs/Connector/Developers/envelope-signature-verification.md new file mode 100644 index 0000000..70637ed --- /dev/null +++ b/docs/Connector/Developers/envelope-signature-verification.md @@ -0,0 +1,117 @@ +--- +title: Envelope Signature Verification +sidebar_position: 8 +--- + +# Envelope Signature Verification + +When a customer grants you support access, TrustedLogin's SaaS hands your Connector an **envelope** — an encrypted blob carrying the bearer identifier your Connector will use to sign the support session into the customer site. Your Connector decrypts the envelope and redirects the support agent into the customer site. + +Envelope **signature verification** is the additional check that proves the envelope you're decrypting actually came from TrustedLogin's SaaS and was not tampered with in transit. The SaaS signs every envelope with an Ed25519 secret key; your Connector verifies the signature with the matching public key before doing anything with the envelope. + +## Why this matters + +Without signature verification, your Connector trusts whatever envelope the SaaS hands it. If TrustedLogin's SaaS is ever compromised — or if a network position lets an attacker substitute one envelope for another — the Connector has no way to detect the swap and will happily redirect the support agent into the wrong site. + +With signature verification configured: + +- Every envelope is cryptographically authenticated as coming from TrustedLogin's SaaS. +- A swapped or tampered envelope is rejected before any decryption happens. +- A compromised SaaS cannot trick your Connector into granting access to a site you didn't ask about. + +## How it works (the happy path) + +You don't have to do anything. As soon as TrustedLogin's SaaS publishes a signing key, your Connector fetches it on the next envelope verification, pins it in the `trustedlogin_vendor_saas_envelope_public_key` option, and verifies every subsequent envelope against that pinned key. + +If TrustedLogin ever rotates the signing key (rare, manually-driven event), the Connector detects the change on the next verification, pins the new key, and continues. The rotation is logged for your records. + +This is the default. Most integrators never need to touch any setting. + +## When you'll see the admin notice + +If your Connector cannot verify envelope signatures — for example, because TrustedLogin's SaaS isn't publishing a signing key, or because your Connector couldn't reach the SaaS to fetch one — the TrustedLogin admin pages display a persistent warning: + +> **Envelope signature verification is off.** Your Connector currently accepts envelopes from TrustedLogin's SaaS without verifying their signatures. Anyone who could substitute or tamper with an envelope in transit could redirect support sessions to the wrong site. + +If you see this notice and TrustedLogin's SaaS *is* signing envelopes, your Connector probably hit a network issue at activation. The next successful envelope verification will retry the fetch and the notice will clear. + +## Manually setting the key + +You only need to do this if: + +- You've disabled automatic key fetching (see below) and want strict out-of-band trust establishment. +- You're running a self-hosted SaaS that publishes the key somewhere other than the standard endpoint. + +Two ways to set the key by hand: + +### Via WP-CLI + +```bash +# Fetch the SaaS's currently-published key. +curl https://app.trustedlogin.com/api/v1/envelope-signing-public-key +# {"publicKey":"a1b2c3...your 64-char hex key here..."} + +# Pin it on the Connector. +wp option update trustedlogin_vendor_saas_envelope_public_key 'a1b2c3...' +``` + +### Via the saas-public-key filter + +If you'd rather keep the key out of the database (e.g. you store it in environment variables alongside other secrets), wire it via the filter: + +```php +add_filter( + 'trustedlogin/connector/envelope/saas-public-key', + function () { + return getenv( 'TRUSTEDLOGIN_SAAS_PUBLIC_KEY' ); + } +); +``` + +The filter takes precedence over the option when both are set. + +## Disabling automatic key fetching + +If you want strict out-of-band trust establishment — e.g. you require a human to verify the key fingerprint before the Connector trusts it — you can disable both the trust-on-first-use fetch and the rotation refetch: + +```php +add_filter( 'trustedlogin/connector/envelope/auto-fetch-key', '__return_false' ); +``` + +With this filter in place, you must set the key manually via WP-CLI or the `saas-public-key` filter. Rotations also become manual: when TrustedLogin announces a key rotation, you set the new key by hand. + +## Soft-mode rollout (escape hatch) + +If you've configured a key but you're staging a SaaS deployment that doesn't sign envelopes yet, you can opt into soft-mode temporarily — unsigned envelopes are accepted with a warning logged to debug.log: + +```php +add_filter( 'trustedlogin/connector/envelope/require-signature', '__return_false' ); +``` + +This is intended only for short rollout windows. Remove the filter as soon as the SaaS side is signing. + +## Hooks reference + +### `trustedlogin/connector/envelope/saas-public-key` + +Filters the hex-encoded sodium public key used to verify SaaS envelope signatures. + +| Parameter | Type | Description | +|---|---|---| +| `$public_key_hex` | `string\|null` | Defaults to the value of the WordPress option `trustedlogin_vendor_saas_envelope_public_key`. Return `null` or an empty string to disable verification entirely (legacy-compat mode). | + +### `trustedlogin/connector/envelope/require-signature` + +Filters whether envelopes must carry a valid signature before the Connector will decrypt them. + +| Parameter | Type | Description | +|---|---|---| +| `$enforce` | `bool` | Defaults to `true` whenever a verification key is configured. Defaults to `false` when no key is configured (you cannot enforce what you cannot verify). Return `false` explicitly to opt back into soft-mode during a rollout. | + +### `trustedlogin/connector/envelope/auto-fetch-key` + +Filters whether the Connector may fetch the SaaS envelope-signing public key automatically. + +| Parameter | Type | Description | +|---|---|---| +| `$enabled` | `bool` | Defaults to `true`. Both trust-on-first-use and the rotation-detection refetch are gated on this filter. Return `false` to require manual key delivery via the option or the `saas-public-key` filter. | diff --git a/docs/Connector/hooks.md b/docs/Connector/Developers/hooks.md similarity index 100% rename from docs/Connector/hooks.md rename to docs/Connector/Developers/hooks.md diff --git a/docs/Connector/running-behind-a-proxy.md b/docs/Connector/Developers/running-behind-a-proxy.md similarity index 99% rename from docs/Connector/running-behind-a-proxy.md rename to docs/Connector/Developers/running-behind-a-proxy.md index 2b16f31..75e9ed4 100644 --- a/docs/Connector/running-behind-a-proxy.md +++ b/docs/Connector/Developers/running-behind-a-proxy.md @@ -148,5 +148,5 @@ The [`trustedlogin/connector/secrets/rate-limit/enabled`](./hooks.md#secrets-rat - [`trustedlogin/connector/trusted-proxies` filter reference](./hooks.md#trusted-proxies) - [`trustedlogin/connector/secrets/rate-limit/enabled` filter reference](./hooks.md#secrets-rate-limit-enabled) -- [Secrets overview](./secrets.md) +- [Secrets overview](../secrets.md) - [Cloudflare IP Ranges](https://www.cloudflare.com/ips/) diff --git a/docs/Connector/encrypted-messages.md b/docs/Connector/encrypted-messages.md deleted file mode 100644 index 8f4894e..0000000 --- a/docs/Connector/encrypted-messages.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Encrypted Messages -sidebar_position: 7 ---- - -# Encrypted Messages - -TrustedLogin now encrypts the notifications your customers' sites send to yours when they grant, extend, or revoke support access. - -## What Changed - -Previously, when a customer clicked "Grant Access," their site sent a plaintext webhook to your Connector — the access key, site URL, and optional debug data were visible in transit and in your server logs. - -Now, those notifications are **encrypted before they leave the customer's site** and buffered on the TrustedLogin SaaS until your Connector picks them up. Your Connector is the only system that can decrypt them. - -## How It Works - -You don't need to change anything. If you're running Connector v1.4+, encrypted messages are enabled automatically alongside the existing webhook for backward compatibility. - -1. **Customer grants access** → their site encrypts the notification with your Connector's public key. -2. **Notification is stored** on TrustedLogin's servers as an opaque blob — TrustedLogin cannot read it. -3. **Your Connector polls** every 5 minutes and decrypts the messages. -4. **Help desk integrations** (Help Scout, FreeScout) receive the decrypted data, same as before. - -## Benefits - -- **No more plaintext in server logs.** The access key and debug data are encrypted end-to-end. -- **Works behind firewalls.** Customers on VPN-only or firewalled networks can now send notifications — they only need outbound access to `app.trustedlogin.com`, which they already have. -- **Reliable delivery.** Messages are buffered for up to 30 days, so a temporary outage on your side doesn't lose notifications. -- **No configuration needed.** The Connector handles polling automatically. - -## Polling Interval - -Your Connector checks for new messages every 5 minutes via WP-Cron. If you need real-time delivery, the TrustedLogin SaaS can push a notification to your Connector to trigger an immediate poll (coming in a future update). - -You can also click "Poll now" on the TrustedLogin Settings page to check immediately. - -## Key Rotation Safety - -If you run "Reset All" on your Connector (which generates new encryption keys), there's a brief window where customer sites may still be using the old public key. TrustedLogin now handles this gracefully — old keys are retained for 20 minutes after rotation, so messages encrypted with the old key can still be decrypted. - -## Backward Compatibility - -During the transition period, the client SDK sends **both** a traditional webhook POST (if configured) and an encrypted message. You can remove your webhook URL from client SDK settings once you've confirmed encrypted messages are working. - -## FAQ - -### Do I need to update the client SDK on customer sites? - -Yes — customers need to be running a client SDK version that supports encrypted messages (v1.10+). Older client SDK versions continue to send plaintext webhooks, which still work. - -### Does this replace Help Scout / FreeScout integration? - -No. Encrypted messages are the *transport* — they replace the plaintext webhook, not the help desk integration. Once your Connector decrypts a message, it fires the same `trustedlogin_connector/message_received` action that your Help Scout or FreeScout integration hooks into. - -### What if the TrustedLogin SaaS is down? - -Messages are buffered on the SaaS for up to 30 days. If the SaaS is temporarily unreachable, the customer's site logs the failure and moves on — the access grant still works, only the notification is delayed. - -### Can TrustedLogin read my messages? - -No. The SaaS stores encrypted bytes it cannot decrypt. Only your Connector's private key can open them. diff --git a/docs/Connector/permissions.md b/docs/Connector/permissions.md new file mode 100644 index 0000000..c81fd92 --- /dev/null +++ b/docs/Connector/permissions.md @@ -0,0 +1,189 @@ +--- +title: Permissions +sidebar_position: 2 +--- + +# Permissions + +The Permissions matrix decides which TrustedLogin features each WordPress role can use. It lives in the sidebar at **TrustedLogin → Permissions** and looks like a grid: one row per role, one column per capability, every cell a `Granted` / `Denied` toggle. + +![The Permissions matrix on a fresh install — Administrator row all granted (and locked); Editor, Author, Contributor, and Subscriber rows all denied.](/img/vendor/permissions/01-overview.png) + +If you've used the plugin before version 1.4 you'll remember a single `approved_roles` setting per team. That gate has been replaced with a real role-based capability system that's auditable, decomposable, and safe by default. Existing teams are migrated automatically — see [Upgrading from `approved_roles`](#upgrading-from-approved_roles) below. + +## Why fine-grained permissions exist + +A typical agency has a handful of people with very different jobs: + +- A **billing manager** needs to look up which client got a support session last week, but should never sign in to a client site themselves. +- A **support lead** signs in to client sites, manages secrets shared with vendors, and reviews activity. +- A **junior support agent** can create new secrets, but shouldn't be able to revoke existing ones their colleagues are using. +- An **integrator** just needs to add and remove team configurations during onboarding — no live access required. + +Before 1.4 there was one switch (the role appeared in `approved_roles`, or it didn't). Now each of those jobs gets exactly the access it needs. + +--- + +## The four capabilities + +Every TrustedLogin capability is prefixed with `trustedlogin_` and granted at the role level (so users with the role inherit the cap). The matrix surfaces all of them: + +### Sign in to client sites (`trustedlogin_decrypt_messages`) + +The sensitive one. Lets the user submit an access key on the **Access Key Log-In** page and assume the support role on a customer site. This is the cap that turns a Connector user into a live operator on someone else's WordPress install. Grant it deliberately. + +### View login activity (`trustedlogin_view_activity`) + +Unlocks the **Login Activity** page. Holders can see the full history of support logins — which agent, which client site, when, and what for — but cannot trigger new logins or change anything else. Read-only by design. + +### Create secrets (`trustedlogin_create_secret`) + +Lets a user generate new one-time secrets from the Secrets page. Strictly weaker than **Manage secrets**: holders can create, but cannot view, edit, or revoke existing secrets. Useful for support agents who need to issue access on demand without being trusted to revoke a colleague's active secret. + +### Manage secrets (`trustedlogin_manage_secrets`) + +Full control over the Secrets page: create, view, and revoke. A user with `manage_secrets` does not automatically get `create_secret` and vice versa — they're two independent toggles, and the Secrets page opens for either one. + +> **What about managing teams?** +> Team configurations live on the **Teams** sub-page, which is restricted to WordPress administrators (`manage_options`) — there's no delegated cap for it on the matrix. Team records hold the SaaS API keypair, webhook secrets, and approved-roles list; anyone who can edit them can effectively impersonate the team to clients. That sensitivity profile matches the Permissions page itself, so both are admin-only by the same rule. + +--- + +## The Administrator row is always granted + +Every role is editable except **Administrator**. The matrix renders that row with a slightly darker background and the cells are disabled. + +![Administrator row close-up — every cell is Granted and disabled, with a slightly darker background that distinguishes it from editable rows.](/img/vendor/permissions/02-admin-row.png) + +There are two reasons: + +1. **No last-admin lockout.** Revoking the wrong cap from administrators could lock the only person with `manage_options` out of pages they need. The plugin is supposed to be recoverable, not bricked, by a misclick on the Permissions page. +2. **Activation re-grants.** On every plugin version bump, the Connector calls `Capabilities::activate()` which re-grants every TL cap to administrators. Letting you toggle the admin row would create a UI that flips back on the next update. + +The server enforces both rules independently — even a forged REST request that names the administrator role and a real cap is rejected with `immutable_role`. The matrix also defends against unicode-homoglyph names like `аdministrator` (Cyrillic `а`); after `sanitize_key()` they collapse to nothing and the request is rejected. + +--- + +## What the matrix does on the wire + +When you click a cell, the panel does an **optimistic flip**: the UI updates immediately so the click feels responsive, then it sends the change to `POST /wp-json/trustedlogin/v1/permissions`. If the server rejects (unknown cap, immutable role), the cell rolls back and an inline error explains why. On success, the cell briefly flashes green so you have explicit confirmation the change was saved — not just registered. + +The endpoint: + +- **gates on `manage_options`** — only WordPress admins can change permissions, period. Holding `trustedlogin_view_activity` is not enough; nor is holding every TL cap stacked together. The Permissions page is for full WP admins only. +- **rejects any cap not in the allowlist** — only the four caps above can be granted via this endpoint, regardless of what's in the request body. You cannot use the Permissions endpoint as a generic "grant arbitrary WP cap" primitive. +- **fires `trustedlogin/connector/capabilities/changed`** on every successful change so audit-log plugins (or your own observers) see the role, cap, granted-or-revoked state, and the user ID of the actor. + +--- + +## Upgrading from `approved_roles` + +If you installed the Connector before 1.4 you used a per-team `approved_roles` array to decide who could sign in to client sites. On the first request after upgrading to 1.4 the plugin runs a one-shot migration: + +- For every role that appeared in any team's `approved_roles`, it grants `trustedlogin_decrypt_messages`. +- It records a flag option (`trustedlogin_connector_approved_roles_migrated`) so the migration runs **exactly once**. +- It fires `trustedlogin/connector/capabilities/changed` for each grant, with actor ID `0` (system). +- If you've already revoked `decrypt_messages` from a role explicitly, the migration won't undo that — it only grants where the cap is missing. + +After migration the access-key login flow checks **both** the team's role list and the cap. Before, either path admitted the user (effectively `OR`). After, both must pass (`AND`) — but because every role that previously cleared `approved_roles` now also holds `decrypt_messages`, no existing user is cut off. + +To verify the migration ran: + +``` +wp option get trustedlogin_connector_approved_roles_migrated +``` + +The value is the timestamp it completed. + +--- + +## Submenu visibility + +The TrustedLogin sidebar entries automatically follow the matrix. A role with **only** `trustedlogin_view_activity` sees just the Login Activity sub-page. A role with **only** `trustedlogin_decrypt_messages` sees just Access Key Log-In. The Settings, Teams, and Permissions sub-pages all require WordPress's `manage_options` cap — those are admin-only regardless of the matrix. + +If a user has zero TL caps, the entire **TrustedLogin** sidebar entry is hidden from them. + +--- + +## Choosing a default landing page + +Each admin can set the default landing page for the **TrustedLogin** menu under Settings. The choices are: Settings (default), Teams, Secrets, Login Activity, Access Key Log-In, Permissions. If you've granted a non-admin user a single TL cap, they automatically land on the page that cap unlocks — they don't have to navigate from Settings (which they can't read) to find the page they can use. + +The fall-through priority for users with multiple caps is: Activity → Secrets → Access Key. So a support lead with both `view_activity` and `manage_secrets` lands on Activity (the page they're most likely to start their day on), while a junior agent with only `create_secret` lands on Secrets. + +--- + +## Filters for integrators + +If you ship an extension that needs its own grantable cap, hook the labels filter: + +```php +add_filter( + 'trustedlogin/connector/capabilities/labels', + function ( $labels ) { + $labels['trustedlogin_export_audit_log'] = array( + 'label' => __( 'Export audit log', 'my-extension' ), + 'description' => __( 'Download the full audit log as CSV.', 'my-extension' ), + ); + return $labels; + } +); +``` + +Three rules the filter enforces: + +1. **The slug MUST start with `trustedlogin_`.** Any other prefix is silently dropped before the matrix renders. This is the allowlist — the Permissions endpoint only mutates caps it knows about, and a non-prefixed cap can't be in the allowlist. +2. **You cannot widen the allowlist to core WP caps** like `manage_options` or `install_plugins`. Even if you return them from the filter, they're stripped. The Permissions UI is not a generic "grant any WP cap" primitive by design. +3. **You handle the cap check yourself in your extension.** The Connector exposes the cap on the matrix and serves the toggle; the actual permission check (`current_user_can( 'trustedlogin_export_audit_log' )`) is yours. + +To audit every change: + +```php +add_action( + 'trustedlogin/connector/capabilities/changed', + function ( $role_slug, $cap, $granted, $actor_id ) { + // $actor_id is 0 for system actions (migration), otherwise a user ID. + my_audit_log()->record( $role_slug, $cap, $granted, $actor_id ); + }, + 10, + 4 +); +``` + +--- + +## Common scenarios + +**"I want a billing manager to see who logged into what client and when, nothing else."** +Grant `trustedlogin_view_activity` to the role they hold (or create a custom role for them). They'll see only the Login Activity sub-page in the TrustedLogin menu. + +**"My support lead needs to do everything except change team configuration."** +Grant `trustedlogin_view_activity`, `trustedlogin_manage_secrets`, and `trustedlogin_decrypt_messages`. Team configuration is restricted to WordPress administrators by default; nothing on the matrix can grant it to a non-admin. + +**"I want a junior agent to issue support links but never revoke anyone else's."** +Grant `trustedlogin_create_secret`. Leave `trustedlogin_manage_secrets` denied. They'll see the Secrets page but only the Create form. + +**"I need to take away client-site access from a role immediately."** +Click `Denied` on the **Sign in to client sites** column for that role. Any active session in flight finishes (the SaaS-issued access key is what carries the authorization, not the role check), but the user can't submit any new access key. + +--- + +## Troubleshooting + +**A cell is disabled and I can't tell why.** +The Administrator row is locked by design — hover the cell for the "Administrator always has every TrustedLogin permission" tooltip. No other rows are disabled in normal use. + +**The toggle clicks but the green flash never appears.** +Check the inline error banner above the table. If it's empty, your browser may be blocking the request — open the network tab and look at the `POST /wp-json/trustedlogin/v1/permissions` response. + +**A user keeps losing access after every plugin update.** +Administrators always get every cap re-granted on update by design (see "The Administrator row"). For other roles, the matrix is the source of truth — caps you've revoked stay revoked across updates. If you're seeing re-grants on a non-admin role, check the audit hook for `actor_id = 0` events; that's the migration, and it only runs once per install. + +**The migration didn't grant a role I expected.** +The migration only runs if the option `trustedlogin_connector_approved_roles_migrated` is empty. If you ran it once, deleted some grants, then upgraded again, those grants stay deleted. To force a re-migration (for example, in a staging install): + +``` +wp option delete trustedlogin_connector_approved_roles_migrated +``` + +Then visit `wp-admin` once. Don't do this on production unless you're sure — it will re-grant `decrypt_messages` to every role that's still in any team's `approved_roles`. diff --git a/docs/Connector/secrets.md b/docs/Connector/secrets.md index 7b78804..e4452d5 100644 --- a/docs/Connector/secrets.md +++ b/docs/Connector/secrets.md @@ -86,7 +86,7 @@ add_filter( 'trustedlogin/connector/trusted-proxies', function ( $ips ) { Entries can be exact IP strings or CIDR ranges (e.g. `192.0.2.0/24`, `2a06:98c0::/29`). Forwarded headers (`X-Forwarded-For`, `CF-Connecting-IP`) are only read when `REMOTE_ADDR` matches an entry here — this prevents unauthenticated clients from spoofing their IP to sidestep rate limits. -For Cloudflare specifically, along with a full explanation of the trust model, see [Running behind a reverse proxy or CDN](./running-behind-a-proxy.md) — it includes a copy-paste snippet covering all Cloudflare edge CIDRs and guidance for nginx / Apache / HAProxy / cloud load balancers. +For Cloudflare specifically, along with a full explanation of the trust model, see [Running behind a reverse proxy or CDN](./Developers/running-behind-a-proxy.md) — it includes a copy-paste snippet covering all Cloudflare edge CIDRs and guidance for nginx / Apache / HAProxy / cloud load balancers. ## Hardening (Optional) diff --git a/docs/Connector/troubleshooting.md b/docs/Connector/troubleshooting.md index 4d46f78..2d312d6 100644 --- a/docs/Connector/troubleshooting.md +++ b/docs/Connector/troubleshooting.md @@ -60,7 +60,7 @@ On the Client site, verify: - Access was actually granted (check the TrustedLogin admin screen) - The access hasn't been revoked - The access period hasn't expired -- The Client site can reach the TrustedLogin SaaS (https://app.trustedlogin.com) +- The Client site can reach the TrustedLogin application (https://app.trustedlogin.com) ## Access key login shows "Redirecting..." but never completes @@ -69,7 +69,7 @@ This usually indicates the POST request to the Client site failed. **Common causes:** 1. **DNS/hosts file issue** - See [Cannot reach domain](#cannot-reach-staging) above 2. **Firewall blocking POST request** - Check firewall rules -3. **Client SDK not initialized** - Client SDK must be initialized early enough (see [Client troubleshooting](/Client/troubleshooting)) +3. **Client SDK not initialized** - Client SDK must be initialized early enough (see [Client troubleshooting](/Developers/Client/troubleshooting)) 4. **Network timeout** - Client site takes too long to respond **Check browser console:** diff --git a/docs/Guides/install-connector.md b/docs/Guides/install-connector.md deleted file mode 100644 index 3768117..0000000 --- a/docs/Guides/install-connector.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -sidebar_label: Install the Connector -sidebar_position: 2 ---- - - - -# Install the Connector and connect your team - -The Connector is a WordPress plugin you install on **your** site — not your customers' sites. It's the bridge between TrustedLogin and the WordPress installations your support team works from. This guide walks through both halves of the setup: copying your API keys from TrustedLogin, then pasting them into the Connector plugin on your own site. - -## 1. Open your team settings on TrustedLogin - -Your dashboard lives at **trustedlogin.com/admin**. Once you're signed in, open **Team Settings** from the sidebar — every value the Connector needs lives on this one page. - -![The Team Settings page on TrustedLogin, with sections for Team identity and API credentials.](/img/guides/install-connector/01-open-team-settings.png) - -## 2. Fill in your team identity - -Three fields live in the **Team identity** card: - -- **Name** — what your customers see when they grant you access. Pick something they'll recognize (usually your product's name). -- **REST API endpoint** — the URL of the WordPress site where the Connector plugin runs, with `/wp-json/` on the end. So if the Connector lives at `https://support.example.com`, enter `https://support.example.com/wp-json/`. -- **Support URL** — where customers land if they need help after a grant expires. A good choice is your product's help desk or contact page. - -![The Team identity card with Name, REST API endpoint, and Support URL fields filled in.](/img/guides/install-connector/02-team-identity.png) - -## 3. Copy your API credentials - -Scroll down to the **API credentials** card. You need three values to paste into the Connector. Each has a **Copy** button next to it. Leave this tab open — you'll need them in a moment. - -![The API credentials card with three numbered callouts on the Team ID, API Key, and Private Key rows.](/img/guides/install-connector/03-api-credentials.png) - -1. **Team ID** — a short number that identifies your team. -2. **API Key** — a 16-character hex string. Public identifier; safe to share. -3. **Private Key** — another 16-character hex string. **Treat this like a password** — anyone with it can connect a site on your team's behalf until you rotate it. - -## 4. Open TrustedLogin in your WordPress admin - -Install the Connector plugin on the WordPress site your support team works from (download the latest release from [the TrustedLogin Connector repository](https://github.com/trustedlogin/trustedlogin-connector/releases), upload the zip via **Plugins → Add New → Upload**, and activate). - -After activation, **TrustedLogin** appears in your WordPress sidebar. On a fresh install it opens the **Create your first team** onboarding screen — one form covers everything you'll paste from TrustedLogin. - -![The Connector's onboarding screen in WordPress admin, headed Create your first team.](/img/guides/install-connector/04-onboarding.png) - -## 5. Paste your Account ID, Public Key, and Private Key - -The onboarding form asks for the three values you just copied. A link below the form — **Where can I find this info?** — takes you back to the right TrustedLogin page if you've lost the tab. - -![The Connector onboarding form with three numbered callouts on Account ID, Public Key, and Private Key, showing the values pasted in.](/img/guides/install-connector/05-paste-credentials.png) - -1. **Account ID** — paste the **Team ID** from TrustedLogin here. -2. **Public Key** — paste the **API Key** from TrustedLogin here. -3. **Private Key** — paste the **Private Key** as-is. - -## 6. Pick the support roles that can use TrustedLogin - -The **What Roles Provide Support?** picker controls which WordPress users on this site can click the Log-in button to reach a customer. **Administrators** is a sensible default for a small support team; larger teams often create a dedicated **Support** role and limit access to that. - -The **Help Desk** dropdown is optional — pick **Help Scout**, **FreeScout**, or leave it as the default if you don't use a help desk. You can change it later; it's not required to finish onboarding. - -![The What Roles Provide Support picker on the onboarding form.](/img/guides/install-connector/06-support-roles.png) - -## 7. Click Continue to confirm the connection - -Click **Continue**. The Connector contacts TrustedLogin with your keys; if everything matches, the next page lists your team with a **Connected** status. - -If you don't see your team listed as connected, the most common cause is a typo or extra whitespace in one of the pasted keys. The second most common is the REST API endpoint on the TrustedLogin side missing the trailing `/wp-json/`. Double-check both before digging further. - -![The cursor on the Continue button, ready to confirm the connection.](/img/guides/install-connector/07-click-continue.png) - -## 8. You're done — head back to your dashboard - -Once your team shows as connected, you're done with setup. Your customers can now grant you secure access, and those grants will show up on your dashboard here. - -You still need to add TrustedLogin to the plugin or theme your customers install on their own sites — that's the piece that puts the **Grant Support Access** button in front of them. See [the Client SDK guide](../Client/01-intro.md) for how to wire it in. - -![The TrustedLogin admin dashboard back at trustedlogin.com, showing the team is ready to receive grants.](/img/guides/install-connector/08-dashboard.png) - -## What if the connection fails? - -Three things to check, in order: - -1. **The REST API endpoint** on your TrustedLogin Team Settings page must include `/wp-json/` at the end. This is the single most common cause. -2. **The keys have no leading or trailing whitespace.** A browser-copied Private Key sometimes picks up an invisible character; paste into a plain text editor first if you're unsure. -3. **The Connector site is reachable from the internet.** TrustedLogin's network needs to hit the REST API endpoint to complete the handshake — a dev install behind a VPN or firewall won't connect. [The Connector troubleshooting guide](../Connector/troubleshooting.md) walks through the less-common failures. diff --git a/docs/Guides/log-in-to-site.md b/docs/Guides/log-in-to-site.md deleted file mode 100644 index 8c0abac..0000000 --- a/docs/Guides/log-in-to-site.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -sidebar_label: Log in to a customer site -sidebar_position: 3 ---- - - - -# Log in to a customer site - -Logging into a customer's WordPress site is the reason TrustedLogin exists. Once a customer has granted you access from their side (see [the Client SDK guide](../Client/01-intro.md) for how that button appears on their end), the grant shows up on your Sites list and you're one click away from being inside their admin. - -## 1. Open the Sites list - -Click **Sites** in the sidebar. Every WordPress site one of your customers has granted you access to shows up here — one row per site, newest grants at the top. - -![Open the Sites list](/img/guides/log-in-to-site/01-open-the-sites-list.png) - - - -## 2. Find the site you want to log into - -Each row shows the site URL, the support role the customer granted, and when the grant expires. Grants have a lifetime (usually a week) so you don't end up with permanent keys to every customer site you've ever helped — if the row says **Expired**, you'll need to ask the customer for a fresh grant before you can log in. - -Use the search box or filters at the top to narrow down to the site you're looking for. The column on the far left is a one-click **Log in** button. - -![Find the site you want to log into](/img/guides/log-in-to-site/02-find-the-site-you-want-to-log-into.png) - - - -## 3. Click Log in - -Clicking the **Log in** arrow opens the customer's WordPress admin in a new tab — already signed in, as a temporary user with the role the customer granted. You don't need to know the customer's password. You don't need them to be online. - -The new tab lands wherever WordPress's default landing is for that role (usually the Dashboard). From there, do whatever support work you need. When you're done, just close the tab — no need to "log out," because the grant itself expires automatically. - -![Click Log in](/img/guides/log-in-to-site/03-click-log-in.png) - - - -## 4. See the full site record - -Click anywhere else on the row — the site URL or the **Edit** action — to see the full site record. This page shows the access key, endpoint URL, who on the customer side granted the access, and every log-in to this particular site. - -The **Log in to site** button at the top does the same thing as the arrow in the list — both open the customer site through the Connector. The **Copy** button next to the access key is audit-logged, so you have a paper trail any time a credential leaves this screen. - -![See the full site record](/img/guides/log-in-to-site/04-see-the-full-site-record.png) - - - -## 5. Revoke access when you are done - -Grants expire on their own, so you rarely need to revoke them manually. But if a customer asks you to (or if something feels wrong — a key appearing in a log, a shared machine, a departing teammate), scroll to the **Danger zone** at the bottom of the site detail page and click **Revoke access**. - -Revoking is immediate and irreversible: the access key stops working, your team can't use it again, and the customer would need to generate a fresh grant if they want you back in. - -![Revoke access when you are done](/img/guides/log-in-to-site/05-revoke-access-when-you-are-done.png) - - - -## What happens if a login doesn't work - -The first thing to check is whether the grant is still active. If the row says **Expired**, the customer needs to generate a new grant — the old one isn't recoverable. - -If the grant shows as active but the Log in button takes you to a WordPress login form instead of landing you signed in, something on the customer's end is wrong: the Connector plugin is likely inactive or the identifier TrustedLogin uses to route the login doesn't match. [The Connector troubleshooting guide](../Connector/troubleshooting.md) covers the common causes. - diff --git a/docs/Guides/regenerate-api-keys.md b/docs/Guides/regenerate-api-keys.md deleted file mode 100644 index 5a63d19..0000000 --- a/docs/Guides/regenerate-api-keys.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -sidebar_label: Regenerate API keys -sidebar_position: 10 ---- - - - -# Regenerate your API keys - -If you think your TrustedLogin Public Key or Private Key has leaked — it showed up in a log, a terminated employee had access to it, or you're just due for a rotation — regenerating is a two-click action. The tradeoff is that your Connector plugin needs updating immediately; keys rotate atomically, not in an overlap window. - -## 1. Open Team Settings - -Rotating keys lives on the **Team Settings** page. Only team **owners** can do this — the action will be hidden if you're signed in as a member or admin. - -![Open Team Settings](/img/guides/regenerate-api-keys/01-open-team-settings.png) - - - -## 2. Find the API credentials section - -Scroll to **API credentials**. The three values you originally pasted into the Connector live here: Account ID, Public Key, and Private Key. You'll regenerate the two keys (the Account ID never changes). - -![Find the API credentials section](/img/guides/regenerate-api-keys/02-find-the-api-credentials-section.png) - - - -## 3. Click Regenerate keys - -**Click Regenerate keys.** TrustedLogin asks for confirmation — this is deliberate, because the **moment you confirm, the old keys stop working.** Any Connector plugin that's still using the old keys will immediately show **Connection failed** until someone updates it. - -Confirm, and TrustedLogin generates a fresh Public Key and Private Key on the spot. - -![Click Regenerate keys](/img/guides/regenerate-api-keys/03-click-regenerate-keys.png) - - - -## 4. Copy the new keys and paste them into the Connector - -Copy the new Public Key and Private Key. In a separate browser tab, open your Connector plugin settings in WordPress (**TrustedLogin** in the sidebar, then **Edit** on the team). Paste the new values over the old ones and save. - -Reload the Teams page in the Connector — you should see **All Teams Connected** again within a few seconds. If you don't, double-check the keys have no leading or trailing whitespace (a common copy-paste issue). - -![Copy the new keys and paste them into the Connector](/img/guides/regenerate-api-keys/04-copy-the-new-keys-and-paste-them-into-the-connector.png) - - - -## 5. Verify access still works - -Back in TrustedLogin, try logging into any of your **Sites**. A successful login from a site means the full chain (TrustedLogin → Connector → customer site) is using the new keys end to end. - -If you do still have the old keys saved somewhere — a password manager, a team notes doc — delete them now. They're dead values; leaving them around invites a copy-paste mistake later. - -![Verify access still works](/img/guides/regenerate-api-keys/05-verify-access-still-works.png) - - - -## When to rotate - -- **Suspected exposure.** A key appeared in a log file, a screen-share recording, a Slack message, a public repo, an error report sent to a vendor — any time a key has touched a surface it shouldn't have, rotate. -- **A teammate left.** If that teammate had access to your Connector plugin settings, rotate on their last day. -- **Periodic hygiene.** Some teams rotate quarterly or after each major release, just as a matter of policy. - -## What the rotation does NOT affect - -Your existing **access grants** from customers are not invalidated by a key rotation. Each grant is scoped to an individual customer site, with its own identifier and expiration — they keep working while the fresh Public and Private Keys flow through the Connector for new grants. - -**Account ID** stays the same. Only the two keys change. - diff --git a/docs/SaaS/01-intro.md b/docs/SaaS/01-intro.md deleted file mode 100644 index 2a2fd22..0000000 --- a/docs/SaaS/01-intro.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: SaaS Intro -sidebar: auto -sidebar_position: 1 ---- - -# TrustedLogin SaaS (Hosted Application) - -The application handles account management, profiles, and billing. The SaaS receives and **processes login and validation requests** from the [Client SDK](../Client/intro) and [TrustedLogin Connector plugin](../Connector/intro). - -## SLA {#service-level-agreement} - -We have a 99.99% uptime commitment for our Enterprise-level customers. Please [read the SLA on our website](https://www.trustedlogin.com/service-level-agreement/). - -## HTTP API {#http-api} - -The TrustedLogin API is [documented on the TrustedLogin website](https://app.trustedlogin.com/docs/api/): - -- [Authenticating requests](https://app.trustedlogin.com/docs/api/#authenticating-requests) -- [Accounts API](https://app.trustedlogin.com/docs/api/#accounts-api) -- [Endpoints](https://app.trustedlogin.com/docs/api/#endpoints) -- [Sites API](https://app.trustedlogin.com/docs/api/#sites-api) - -There's also a [Postman collection](https://app.trustedlogin.com/docs/collection.json) available. - -## Server Setup {#server-setup} - -The TrustedLogin application is powered by Laravel and run on a Dockerized, high-availability, Kubernetes cluster. - -**[Learn more about the Server Setup](./server-setup)** diff --git a/docs/SaaS/CI-CD.md b/docs/SaaS/CI-CD.md deleted file mode 100644 index 2c35c66..0000000 --- a/docs/SaaS/CI-CD.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: CI/CD -sidebar_position: 2 -draft: true ---- -# Continuous Integration and Continuous Deployment (CI/CD) - -Each pull request will trigger a build in ChipperCI for testing and a QA site on dev Kubernetes cluster. - -## What We Are Using {#what-we-are-using} - -* [Digital Ocean](https://www.digitalocean.com/) - * Hosts the app - * Provides a QA environment for each branch on different non production cluster. -* GitHub - * Hosts the code and issue tracker. - * Runs automated testing. -* [PHPUnit](https://phpunit.de/) - * Used for PHP tests, including browser tests. - * [Laravel Testing Docs](https://laravel.com/docs/5.8/testing) - * [Browser Testing Docs](https://laravel.com/docs/5.8/dusk) - -## GitHub {#github} - -Whenever you push to a branch containing an open pull request: - -1. GitHub Actions will run tests using PHPUnit. These include unit/integration tests as well as browser tests using Dusk. -2. Platform will deploy an environment that you can use for manual QA and testing. - -## Kubernetes + Github Actions {#kubernetes--github-actions} - -We utilize GitHub Actions in conjunction with Helm to deploy application code to a production-grade Kubernetes cluster. Upon creating a pull request, a new QA environment is generated, and its URL is accessible based on your GitHub branch name. Developers conduct testing in the dev/test environment, undergo peer review. - -### Deploying to Master {#deploying-to-master} - -To deploy to master, merge any open PR to master. Upon approval, the code is merged into the master branch. Subsequently, the CI/CD process initiates, deploying the code to the production Kubernetes cluster. - -### Troubleshooting Kubernetes deploy issues. {#troubleshooting-kubernetes-deploy-issues} - -If a pull request does _not_ create a new build in dev Kubernetes, there are a few reasons to look at first: - -1. Check if the Docker build for the application succeeded. -2. Check if all the necessary infrastructure is created in a namespace related to your Git branch in the dev cluster. -3. Check if the deploy stage succeeded in GitHub Actions. diff --git a/docs/SaaS/cli.md b/docs/SaaS/cli.md deleted file mode 100644 index cc78f63..0000000 --- a/docs/SaaS/cli.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: CLI -sidebar_position: 3 -draft: true ---- -# Command Line Interface - -Query the API using the command line interface. - -## List All Accounts (teams) {#list-all-accounts-teams} -* List All Accounts - * `php artisan accounts:list` - -## Lists Sites {#lists-sites} -* List All Sites - * `php artisan sites:list` -* List All Sites Belonging To A Specific Account - * `php artisan sites:list 6` - - -## Add An Elasticsearch Index For Team {#add-an-elasticsearch-index-for-team} - -* `php artisan team:index {team}` -* `php artisan team:index 6` diff --git a/docs/SaaS/elasticsearch.md b/docs/SaaS/elasticsearch.md deleted file mode 100644 index 6ceae32..0000000 --- a/docs/SaaS/elasticsearch.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -draft: true ---- - -# Elasticsearch - -Elasticsearch is used to log access and usage data, as well as to display that data in the UI. - -## Servers {#servers} - -For production, set environment variable for `ELASTIC_SEARCH_HOST` to URL for Elasticsearch instance, with basic auth credentials in URL. The default is the docker-compose URL. - -### Local Development {#local-development} - -Elasticsearch is included in the local docker-compose environment. Inside of Docker use http://elasticsearch:9200 to connect. - -### Production {#production} - -The production Elasticsearch is at https://elasticsearch.trustedlogin.com - -## Clients And Services {#clients-and-services} - - -`App\Http\Clients\ElasticSearch` is an HTTP client for Elasticsearch, which is decopupled from TrustedLogin's business logic. `App\Services\ElasticSearchTeamData` uses that client. It provides methods for writing and searching team data, as well as methods for creating and updating team indexes. - -### Dependency Injection {#dependency-injection} - -All of this is setup in `App\Providers\ElasticSearchProvider`, so you can type hint the interface `App\Contracts\ElasticSearchTeamData` to inject the server. - -In most Laravel controller, you can bind that service to the constructor of - -```php -esClient = $esClient; - } - - public function endpoint(Team $team){ - //Add an array of data to the log. - //Must use an "eventType" field. - $this->esClient->writeDataForTeam( - $team, - [ - //always set eventType! - 'eventType' => ElasticSearchEventTypes::SOMETHING_YOU_ADDED, - //add other stuffs - 'sandwich' => 'special', - 'drinks' => ['seltzer', - ['coke' => 'diet' ] - ], - ] - ); - - } - - public function endpointThatLogsLogin(Team $team, Site $site){ - - - //Log a login to a site - $this->esClient->logLogin( - $team, - $site, - now()//optional, carbon instance, default is now() - ); - } -} -``` - -### Event Types {#event-types} - -The search queries that the `ElasticSearchTeamData` service makes assume a meaningful "eventType" column is in the data for the index. So, when logging data for a team, you need to add an eventType. - -Event types must be registered in `ElasticSearchEventTypes`. Before adding a new event type, add a constant to that class with the name. Then add that constant to the array in the `getTypes()` method. diff --git a/docs/SaaS/server-setup.md b/docs/SaaS/server-setup.md deleted file mode 100644 index feaacab..0000000 --- a/docs/SaaS/server-setup.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Server Setup -sidebar: auto -sidebar_position: 2 -draft: true ---- - -# TrustedLogin SaaS Server Setup - -The TrustedLogin application is powered by Laravel and run on a Dockerized, high-availability, Kubernetes cluster. - -The application handles account management, profiles, and billing. But most important, it receives and processes requests from the [Client SDK](/Client/intro) and [TrustedLogin Connector plugin](/Connector/intro). - -## What software powers TrustedLogin? {#what-software-powers-trustedlogin} - -| Software | URL | Description | -|-----------------------|-------------------------------------------------------------|-----------------------------------| -| Helm | https://helm.sh/ | Kubernetes deployment | -| Docker | https://www.docker.com/ | Container management | -| Kubernetes | https://kubernetes.io/ | Container orchestration | -| Traefik | https://traefik.io/ | Load balancing & reverse proxy | -| Jetstack Cert Manager | https://github.com/jetstack/cert-manager | Kubernetes certificate management | -| Laravel | https://laravel.com/ | Web application framework | -| Laravel Spark | https://spark.laravel.com/ | Billing & portal | -| Laravel Dusk | https://dusk.laravel.com/ | Acceptance tests | -| Guzzle | https://packagist.org/packages/kozz/laravel-guzzle-provider | HTTP client | -| Hashicorp Vault | https://www.vaultproject.io/ | Key management | -| Elasticsearch | https://www.elastic.co/products/elasticsearch | Log search | -| Kibana | https://www.elastic.co/products/kibana | Log analysis | -| Velero | https://velero.io | Backup | -| MySQL | https://www.mysql.com/ | Database | -| Datadog | https://www.datadoghq.com/ | Log aggregation | -| Redis | https://redis.io/ | Caching | -| Prometheus | https://prometheus.io/ | Metrics & alerting | - - - -## Additional information {#additional-information} - -### [Helm](https://github.com/helm/helm) {#helm} - -The TrustedLogin Kubernetes deployment is structured using Helm. - -### [Kubernetes](https://kubernetes.io/) {#kubernetes} - -The application is hosted on [managed DigitalOcean Kubernetes](https://www.digitalocean.com/products/kubernetes/) ("DOKS"). - -### [Traefik](https://traefik.io) {#traefik} - -Traefik acts as a reverse-proxy load balancer. - -- Redirects requests from http to https -- To restrict access based on IP addresses -- Routes traffic various services within Kubernetes account to url requested - -### [Hashicorp Vault](https://www.vaultproject.io) {#hashicorp-vault} - -TrustedLogin uses Vault as a key management system. It is configured with three fallback nodes to provide high-availability. Vault is configured to be auto-unsealing. - -### [Velero](https://velero.io) {#velero} - -Velero backs up the Kubernetes deployment every six hours to Digital Ocean Spaces. We maintain backups for 3 days (72 hours). - -### [Laravel Spark](https://spark.laravel.com) {#laravel-spark} - -The eCommerce and public-facing UI are powered by Laravel Spark. - -### [MySQL](https://www.mysql.com) {#mysql} - -The Laravel Spark database uses Digital Ocean Managed MySQL with High Availability. Account, profile, and and Stripe billing metadata are stored in MySQL. - -### [Elasticsearch](https://www.elastic.co/products/elasticsearch) {#elasticsearch} - -Logs stripped of PII are added to Elasticsearch for later analysis. [See Elasticsearch documentation](/SaaS/elasticsearch) for more information. - -### [Redis](https://redis.io/) {#redis} - -Laravel uses redis as a caching mechanism for temporary data storage to speed up the process of performing database queries and getting feedback, which will, in turn, reduce the amount of time spent pulling up data. Our applicaiton relies on Digital Ocean fully managed redis instance in high availability mode. diff --git a/docs/SaaS/user-remote-authentication.md b/docs/SaaS/user-remote-authentication.md deleted file mode 100644 index d375372..0000000 --- a/docs/SaaS/user-remote-authentication.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -draft: true ---- - -# Remote User Authentication - -In the [TrustedLogin Connector plugin](../Connector/) (the support-side plugin), users must log into the SaaS app. This document explains how. - -## Get The Account's ClientId and Client Secret {#get-the-accounts-clientid-and-client-secret} - -### Get The Client ID and Secret {#get-the-client-id-and-secret} -Each account has an oAuth client of the [password grant type](https://laravel.com/docs/5.8/passport#password-grant-tokens). The vendor side plugin should have settings for API key, Public Key and account id. These are all visible in team settings in SaaS app. - -To obtain this information, make a GET request to `/api/accounts/` where accountId is the ID of the account. This request should set, in the `X-TL-TOKEN` header, the `sha256` hash of the public key joined, without spaces to the api key. - -```php -$hash = hash( 'sha256', $publicKey . $apiKey ); -``` - -That response will include `clientId` and `clientSecret`. You will need them to login. It also includes the readToken for the team. - -![team-keys](https://user-images.githubusercontent.com/1994311/63133352-dddeb000-bf92-11e9-96a5-8b99b5d1b378.PNG) - - -### Login a User {#login-a-user} - -Then you can use that to make a POST request to request an oath token and the accounts read token to `/oauth/token`. With this body: - -```json -{ - 'grant_type' => 'password', - 'client_id' => 'client-id', // this is the clientId from above - 'client_secret' => 'client-secret', //this is the clientSecret from above - 'username' => 'roy@hiroy.club', //user name - 'password' => 'my-password', //userpassword, - 'scopes' => '*' -} -``` - -If the login is successful, a `200` status code and a response like this will be returned: - -```json -{ - "token_type": "Bearer", - "expires_in": 259199, - "access_token": "very-long-string", - "refresh_token": "also-very-long" -} -``` - -The `access_token` is very long, and should be stored for later. - - -### Verify Token {#verify-token} - -To verify that a token is still valid, use it as a bearer token to request user details by making a GET request to `api/user`. - -Headers: -```php -[ - 'Accept' => 'application/json', - 'Authorization' => 'Bearer '. $accessToken, -], -``` - -This will return the current username, ID, and email if valid. diff --git a/docs/SaaS/vault-client.md b/docs/SaaS/vault-client.md deleted file mode 100644 index b45cecb..0000000 --- a/docs/SaaS/vault-client.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -draft: true ---- - -# Vault API Client - -The Vault API client is used to make HTTP requests to the Vault instance. - -## Team Key Store {#team-key-store} - -### Creation of the Keystore {#creation-of-the-keystore} - -When an account is created, Spark's `TeamCreated` event fires. A keystore is created in Vault with the slug column from the team model used as the namespace in Vault. - -### Creation Of The Keystore Policies {#creation-of-the-keystore-policies} -Then three policies are created for the at key store. - -The first, "The Write Policy" is created with create, read and delete capabilities. The policy name is `-write-policy`. - -A second policy "The Delete Policy" is created with the capability of delete. The policy name is `-delete-policy`. - -A third policy, which completes the trilogy, "The Read Token" is created with the read capability. The policy name is `-read-policy`. - -### The Keystore Tokens {#the-keystore-tokens} -This also schedules a job to update the team's tokens. This job will repeat once a day. - -The job that updates the team's tokens is concerned with three tokens. The first token, "The Write Token" uses the write policy. The second token, "The Delete Token" uses the delete policy. The third token, "The Read Token" used the read policy. - -#### Access Team Tokens {#access-team-tokens} - -**PHP** - -```php -use \App\Team; -$team = Team::find(42); -$deleteToken = $team->deleteToken(); -$writeToken = $team->writeToken(); -``` \ No newline at end of file diff --git a/docs/SaaS/vault-sass-token.md b/docs/SaaS/vault-sass-token.md deleted file mode 100644 index b9251cf..0000000 --- a/docs/SaaS/vault-sass-token.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -draft: true ---- - -# The Vault SaaS Token - -When accessing Vault, this application uses a special, highly privileged token, we call the "SaaS token". - -## Using The SaaS Token {#using-the-saas-token} - -The SaaS token is stored in the environment variable `VAULT_TOKEN` in Kubernetes secrets. In PHP code it can be accessed like this: - -```php -$token = env('VAULT_TOKEN'); -``` - -### Resetting The SaaS Token {#resetting-the-saas-token} - -For local development, edit you `.env` file: - -```txt -VAULT_TOKEN=trustedlogin -``` - -:::warning -Make sure the `VAULT_TOKEN` variable is set to be "inheritable" and "sensitive". -::: - -GitHub Actions has local `VAULT_TOKEN` and `VAULT_URL` environment variables and do not need to be modified. - -#### Links {#links} - -* [Storing environment variables in GitHub Secrets](https://github.com/trustedlogin/trustedlogin-ecommerce/settings/secrets/actions) Application secrets, including the Vault SaaS token, are stored in GitHub secrets, which are pulled into Kubernetes during deployment. -* [Setting variables using Helm](https://github.com/trustedlogin/trustedlogin-ecommerce/blob/master/.github/workflows/on-merge-deploy-to-prod.yml#L75) Environment variables are defined by Helm charts during deployment. - -Only project admin can modify GitHub secrets. - -#### Notes {#notes} - -Access to environment secrets is limited to GitHub admins. diff --git a/docs/SaaS/vault.md b/docs/SaaS/vault.md deleted file mode 100644 index 5858585..0000000 --- a/docs/SaaS/vault.md +++ /dev/null @@ -1,389 +0,0 @@ ---- -draft: true ---- - -# Working with Vault - -An instance of [HashiCorp Vault](https://www.vaultproject.io) is used to store all sensitive data about customer sites. - -:::info -* The URL of Vault is: `https://vault.trustedlogin.com` -* The "app's auth token" Refers to the token used to identify this app, with Vault. - * This token should grant the ability to take these actions and no others. -* The "SaaS Token" is [documented here](./vault-sass-token.md) -::: - -## Check TTL Of Auth Token {#check-ttl-of-auth-token} -To check the TTL of the current token used by the app to authenticate against vault: - -* [API Docs](https://www.vaultproject.io/api/auth/token/index.html#lookup-a-token-self-) -* URI - `/v1/auth/token/lookup-self` -* HTTP Method - `GET` -* Required Headers `'X-Vault-Token':` -* Request Body: None - -* Example Response: -```json -{ - "request_id": "ac4a1ed6-64f7-6cca-2f38-182efdf37e4a", - "lease_id": "", - "renewable": false, - "lease_duration": 0, - "data": { - "accessor": "X9H1u4IrPFgbUmjhToXtH5mq", - "creation_time": 1557336326, - "creation_ttl": 0, - "display_name": "root", - "entity_id": "", - "expire_time": null, - "explicit_max_ttl": 0, - "id": "s.KODayxpVxBLBw8MxZdRKTU7r", - "meta": null, - "num_uses": 0, - "orphan": true, - "path": "auth/token/root", - "policies": [ - "root" - ], - "ttl": 0, - "type": "service" - }, - "wrap_info": null, - "warnings": null, - "auth": null -} -``` - -## Renew Auth Token {#renew-auth-token} -To renew the app's auth token for longer use: - -* [API Docs](https://www.vaultproject.io/api/auth/token/index.html#renew-a-token-self-) -* URI - `/v1/auth/token/renew-self` -* HTTP Method - `POST` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "increment": "42h" -} -``` - -* Example Response: -```json -{ - "request_id": "e113b883-0241-e8f0-39a9-b1fded129c63", - "lease_id": "", - "renewable": false, - "lease_duration": 0, - "data": null, - "wrap_info": null, - "warnings": null, - "auth": { - "client_token": "s.XWBIrPzWxZaobgtZRwwV0der", - "accessor": "DSHxCZA9fWVjtKIBmNhEsQ1x", - "policies": [ - "default", - "saas-policy" - ], - "token_policies": [ - "default", - "saas-policy" - ], - "metadata": null, - "lease_duration": 151200, - "renewable": true, - "entity_id": "", - "token_type": "service", - "orphan": false - } -} -``` - -## Request That A Key Store Be Created For Vendor {#request-that-a-key-store-be-created-for-vendor} -Vault can have a specific database or key store per vendor. When a vendor signs up, a new key store is created. - -:::note -The Key Store will be called `-store` where `` is a variable identifying the Client's project. (eg Key Store name = `gravityview-store` for `GravityView` project.). It is in the URI. -::: - -To request a new key store for a vendor: - -* [API Docs](https://www.vaultproject.io/api/system/mounts.html#enable-secrets-engine) -* URI - `/sys/mounts/-store` -* HTTP Method - `POST` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "type": "kv", - "description":"Key Store for client X", - "options": { - "version": "1" - } -} -``` - -* Example Response: 204 - No Content - -Once this is received, create policies for Client Plugin Token and Delete Site permissions: - -:::note -1. Policy will be called `-write-policy` where `` is a variable identifying the Client's project. (eg Policy name = `gravityview-write-policy` for `GravityView` project.). This is in the URI. -2. The Key Store name from before `-store` is used in the Request Body. -::: - -* [API Docs](https://www.vaultproject.io/api/system/policies.html#create-update-acl-policy) -* URI - `/v1/sys/policies/acl/-write-policy` -* HTTP Method - `PUT` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "policy": "path \"-store/*\" { capabilities = [\"create\", \"update\", \"delete\"]}" -} -``` - -* Example Response: 204 - No Content - -and: - -:::note -1. Policy will be called `-delete-policy` where `` is a variable identifying the Client's project. (eg Policy name = `gravityview-delete-policy` for `GravityView` project.). This is in the URI. -2. This policy is used to create a token that extends SaaS privileges to be able to remove Sites from Key Store. -3. The Key Store name from before `-store` is used in the Request Body. -::: - -* [API Docs](https://www.vaultproject.io/api/system/policies.html#create-update-acl-policy) -* URI - `/v1/sys/policies/acl/-delete-policy` -* HTTP Method - `PUT` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "policy": "path \"-store/*\" { capabilities = [\"delete\"]}" -} -``` - -* Example Response: 204 - No Content - -and: - -:::note -1. Policy will be called `-read-policy` where `` is a variable identifying the Client's project. (eg Policy name = `gravityview-read-policy` for `GravityView` project.). This is in the URI. -2. This policy is used to create a token for Support-side plugin to read secrets from ONLY `-store`. -3. The Key Store name from before `-store` is used in the Request Body. -::: - -* [API Docs](https://www.vaultproject.io/api/system/policies.html#create-update-acl-policy) -* URI - `/v1/sys/policies/acl/-read-policy` -* HTTP Method - `PUT` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "policy": "path \"-store/*\" { capabilities = [\"read\"]}" -} -``` - -* Example Response: 204 - No Content - - -## Request Short-Term Token For Client Plugin To Create Site With {#request-short-term-token-for-client-plugin-to-create-site-with} -The client plugin will make requests to this app's `POST /api/site` endpoint to request a token it can use to create site in Vault. - -To get that token from Vault: - -:::note -`-write-policy` in Request Body was defined above and tells the token what it can/can't access -::: - -* [API Docs](https://www.vaultproject.io/api/auth/token/index.html#create-token) -* URI - `/v1/auth/token/create` -* HTTP Method - `POST` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "policies": [ - "-write-policy" - ], - "metadata": { - "key": "value", - }, - "ttl": "7d", - "renewable": true, - "display_name": "Write Token", - } -``` - -* Example Response: -```json -{ - "request_id": "...", // string - "wrap_info": null, - "warnings": null, - "auth": { - "client_token": "...", // string - "accessor": "...", // string - "policies": [ - "default", - "gravityview-write-policy" // the policy defining permissions - ], - "token_policies": [ - "default", - "gravityview-write-policy" // the policy defining permissions - ], - "metadata": null, - "lease_duration": 1296000, - "renewable": true, - "entity_id": "", - "token_type": "service", - "orphan": false - } -} -``` - -## Request Access Logs For A Site {#request-access-logs-for-a-site} -To get access logs for a specific site, app can make a this request to Vault: - -:::warning -This documentation is incomplete. We are working on it. -::: - -* URI - `/v1/...` -* HTTP Method - ... -* Required Headers ... -* Request Body: -```json -{ - "hi": "roy" -} -``` - -* Example Response: -```json -{ - "hi": "roy" -} -``` - -## Request That A Site Be Removed Form Vault {#request-that-a-site-be-removed-form-vault} -If the app needs to remove record of a site login, it can make this request to Vault: - -First need to create a temporary token with delete permissions. Then use this Token to give SaaS the ability to delete Site info. - -:::note -`` (the SaaS token) is used in Headers. `-delete-policy` in Request Body was defined above and tells the token what it can/can't access -::: - -To get that token from Vault: - -* [API Docs](https://www.vaultproject.io/api/auth/token/index.html#create-token) -* URI - `/v1/auth/token/create` -* HTTP Method - `POST` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "policies": [ - "-delete-policy" - ], - "ttl": "1d", - "renewable": false, - "display_name": "Delete Token", - } -``` - -* Example Response: -```json -{ - "request_id": "...", - "wrap_info": null, - "warnings": null, - "auth": { - "client_token": "", - "accessor": "...", - "policies": [ - "default", - "gravityview-delete-policy" - ], - "token_policies": [ - "default", - "gravityview-delete-policy" - ], - "metadata": null, - "lease_duration": 1296000, - "renewable": true, - "entity_id": "", - "token_type": "service", - "orphan": false - } -} -``` - -Then use that generated token to delete the secret: - -:::note -1. `` (from above) is used in Headers instead of ``. -2: `` is provided from Client-side plugin in the request body sent to SaaS via `POST /sites` -::: - -* [API Docs](https://www.vaultproject.io/api/secret/kv/kv-v1.html#delete-secret) -* URI - `/v1/-store/` -* HTTP Method - `DELETE` -* Required Headers `'X-Vault-Token': ` -* Request Body: None - -* Example Response: 204 - No Content - -## Request Short-Term Token For Support-side Plugin To Read Site data {#request-short-term-token-for-support-side-plugin-to-read-site-data} -The support-side plugin will make requests to the SaaS app to get a token it can use to read sites in Vault. - -To get that token from Vault: - -:::note -`-read-policy` in Request Body was defined above and tells the token what it can/can't access -::: - -* [API Docs](https://www.vaultproject.io/api/auth/token/index.html#create-token) -* URI - `/v1/auth/token/create` -* HTTP Method - `POST` -* Required Headers `'Content-Type':'application/json','X-Vault-Token':` -* Request Body: -```json -{ - "policies": [ - "-read-policy" - ], - "ttl": "24h", - "renewable": true, - "display_name": "Support-side Token", - } -``` - -* Example Response: -```json -{ - "request_id": "...", // string - "wrap_info": null, - "warnings": null, - "auth": { - "client_token": "...", // string - "accessor": "...", // string - "policies": [ - "default", - "gravityview-write-policy" // the policy defining permissions - ], - "token_policies": [ - "default", - "gravityview-write-policy" // the policy defining permissions - ], - "metadata": null, - "lease_duration": 1296000, - "renewable": true, - "entity_id": "", - "token_type": "service", - "orphan": false - } -} -``` diff --git a/docs/SaaS/webhooks.md b/docs/SaaS/webhooks.md deleted file mode 100644 index 089a45b..0000000 --- a/docs/SaaS/webhooks.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -draft: true ---- - -# Webhooks - -There are webhooks that are fired at key customer life-cycle events so that TrustedLogin can integrate with Mailchimp and other sorts of marketing automation or reporting tools without modifying the codebase. - -## Editing Webhook Settings {#editing-webhook-settings} -There is a nova page called "Settings". It has settings pages - -## Getting Webhook Settings {#getting-webhook-settings} - -The class `App\Contracts\GetSettingsContract` is a wrapper for the settings these webhooks use. - -```php -use App\Contracts\GetSettingsContract; -use App\Interactions\GetSettings; -$settings = app()->get(GetSettingsContract::class); -$webhookUrl = $settings->getWebhook( 'webhook_site_created' ); -``` - -## Technical Details {#technical-details} -The webhook server is built with [spatie/laravel-webhook-server](https://github.com/spatie/laravel-webhook-server) - -The site events are triggered inside the `SiteObserver`. - -The account webhooks are triggered using listeners attached to Spark events. - -## Site-Related Webhooks {#site-related-webhooks} -The payload for these webhooks looks like this: - -```json -{ - "id" : "7", - "account" : "901", - "url": "https://industry.io" -} -``` - -### Site Created {#site-created} -This event fires after a new site is created. - -### Site Deleted {#site-deleted} -This event fires after a site is deleted. - -### Account Created {#account-created} -This event fires after an account is created. - -## Account-Related Webhooks {#account-related-webhooks} -The payload for these webhooks looks like this: - -```json -{ - "id" : 7, - "name" : "Thunder Bubble", - "publicKey" : "1234-7890-12345", - "namespace" : "thunder-bubble", - "ownerName" : "Trover DuChamps", - "ownerEmail" : "trover@industry.io", - "trial" : false, - "hasCard" : true, - "isSubscribed" : true -} -``` - -### Account Cancelled {#account-cancelled} -This event fires after an account is updated. - -### Account Renewed {#account-renewed} -This event fires after an account subscription is renewed. - -## User-Realted Webhook {#user-realted-webhook} -The payload for these webhooks looks like this: - -## User Created {#user-created} - -```json -{ - "id" : 7, - "name" : "Duke Corknelius Von Canadia", - "email" : "hiroy@electronic-email-service.com" -} -``` - -## User Deleted {#user-deleted} - -```json -{ - "id" : 7, - "name" : "Duke Corknelius Von Canadia", - "email" : "hiroy@electronic-email-service.com" -} -``` \ No newline at end of file diff --git a/docs/for-ai-tools.md b/docs/for-ai-tools.md index 2b96261..6883b44 100644 --- a/docs/for-ai-tools.md +++ b/docs/for-ai-tools.md @@ -13,9 +13,9 @@ Append `.md` to any URL on this site to fetch the raw Markdown source instead of | Rendered HTML | Raw Markdown | | --- | --- | -| [`/Client/intro`](/Client/intro) | [`/Client/intro.md`](pathname:///Client/intro.md) | -| [`/Client/integration-prompt`](/Client/integration-prompt) | [`/Client/integration-prompt.md`](pathname:///Client/integration-prompt.md) | -| [`/Client/troubleshooting`](/Client/troubleshooting) | [`/Client/troubleshooting.md`](pathname:///Client/troubleshooting.md) | +| [`/Client/intro`](/Developers/Client/intro) | [`/Client/intro.md`](pathname:///Client/intro.md) | +| [`/Client/integration-prompt`](/Developers/Client/integration-prompt) | [`/Client/integration-prompt.md`](pathname:///Client/integration-prompt.md) | +| [`/Client/troubleshooting`](/Developers/Client/troubleshooting) | [`/Client/troubleshooting.md`](pathname:///Client/troubleshooting.md) | The Markdown form has the same content as the rendered page, with public-friendly frontmatter (`title`, `description`, `keywords` only — Docusaurus-internal fields like `sidebar_position` are stripped). Admonitions like `:::tip` and `:::warning` are preserved verbatim — most Markdown parsers handle them transparently. diff --git a/docs/security.md b/docs/security.md index eb45e79..1443ad3 100644 --- a/docs/security.md +++ b/docs/security.md @@ -7,7 +7,7 @@ sidebar_position: 2 # Security :::info -For Client SDK security, see [Client SDK](/Client/security). +For Client SDK security, see [Client SDK](/Developers/Client/security). ::: ## Encryption {#encryption} @@ -23,14 +23,14 @@ Because cryptobox encryption cannot verify the identity of the sender, during de Secrets are encrypted and stored using the [Sodium Secret Box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) algorithm in the Hashicorp Vault. :::note -For more information around data storage, see [SaaS Data Storage](/SaaS/data-storage). +For more information around data storage, see [Account Data Storage](/Account/data-storage). ::: -## SaaS application security {#saas-application-security} +## Account application security {#saas-application-security} ### IP restrictions {#ip-restrictions} -The SaaS Vault, Elasticsearch, and Kibana are protected behind IP restrictions using Traefik. [See how Traefik is used](/SaaS/server-setup#traefik). +The Account application's Vault, Elasticsearch, and Kibana services are protected behind IP restrictions using Traefik. ### Strong-password policy {#strong-password-policy} diff --git a/docusaurus.config.js b/docusaurus.config.js index 44fd20d..7a4590f 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -64,26 +64,112 @@ const config = { [ '@docusaurus/plugin-client-redirects', { - // SaaS internal infrastructure pages were previously published. They - // describe Vault tokens, Kubernetes secrets, deployment internals, - // etc. and shouldn't be public — they're now drafted (so they 404 in - // production) but old bookmarks and external links still exist. - // Redirect each to /SaaS/intro (the public SaaS overview). + // Two redirect groups: // - // Note: GitHub Pages can't issue real HTTP 301s, so this plugin emits - // a small HTML page at each `from` URL that meta-refreshes + JS - // navigates to `to`. Search engines treat it as a redirect (SEO - // weight slightly less than a true 301, but functional for users). + // 1. /SaaS/* → /Account/* — the section was renamed from + // "SaaS" (architecture jargon) to "Account" (what users + // actually call it). External links and search-engine + // indexes pointing at the old paths are preserved. + // + // 2. The internal-infrastructure pages (Vault, K8s secrets, + // deployment internals, etc.) that used to live in this + // section. Drafted out of production but old bookmarks + // still exist; they all collapse to /Account/intro now. + // + // GitHub Pages can't issue real HTTP 301s, so this plugin + // emits a small HTML page at each `from` URL that meta- + // refreshes + JS-navigates to `to`. Search engines treat it + // as a redirect (SEO weight slightly less than a true 301, + // but functional for users). redirects: [ - { from: '/SaaS/vault-sass-token', to: '/SaaS/intro' }, - { from: '/SaaS/vault', to: '/SaaS/intro' }, - { from: '/SaaS/vault-client', to: '/SaaS/intro' }, - { from: '/SaaS/CI-CD', to: '/SaaS/intro' }, - { from: '/SaaS/server-setup', to: '/SaaS/intro' }, - { from: '/SaaS/cli', to: '/SaaS/intro' }, - { from: '/SaaS/elasticsearch', to: '/SaaS/intro' }, - { from: '/SaaS/webhooks', to: '/SaaS/intro' }, - { from: '/SaaS/user-remote-authentication', to: '/SaaS/intro' }, + // Group 1: section rename, page-by-page identity map + { from: '/SaaS/intro', to: '/Account/intro' }, + { from: '/SaaS/data-storage', to: '/Account/data-storage' }, + { from: '/SaaS/subcontractors', to: '/Account/subcontractors' }, + + // Group 2: zombies, all to /Account/intro (preserved + // typo entry for vault-sass-token covers an old misspelled + // bookmark in the wild) + { from: '/SaaS/vault-sass-token', to: '/Account/intro' }, + { from: '/SaaS/vault-saas-token', to: '/Account/intro' }, + { from: '/SaaS/vault', to: '/Account/intro' }, + { from: '/SaaS/vault-client', to: '/Account/intro' }, + { from: '/SaaS/CI-CD', to: '/Account/intro' }, + { from: '/SaaS/server-setup', to: '/Account/intro' }, + { from: '/SaaS/cli', to: '/Account/intro' }, + { from: '/SaaS/elasticsearch', to: '/Account/intro' }, + { from: '/SaaS/webhooks', to: '/Account/intro' }, + { from: '/SaaS/user-remote-authentication', to: '/Account/intro' }, + + // Drop the now-deprecated /Account/* internal pages too + // (they remain on disk but should not surface as + // navigable URLs once the audit-and-relocate follow-up + // happens). + { from: '/Account/vault-saas-token', to: '/Account/intro' }, + { from: '/Account/vault', to: '/Account/intro' }, + { from: '/Account/vault-client', to: '/Account/intro' }, + { from: '/Account/CI-CD', to: '/Account/intro' }, + { from: '/Account/server-setup', to: '/Account/intro' }, + { from: '/Account/cli', to: '/Account/intro' }, + { from: '/Account/elasticsearch', to: '/Account/intro' }, + { from: '/Account/webhooks', to: '/Account/intro' }, + { from: '/Account/user-remote-authentication', to: '/Account/intro' }, + + // Group 3: Guides moved from /Guides/* (top-level, drafted but + // never published) into /Account/Guides/*. Redirects are + // belt-and-suspenders for any leaked link. + { from: '/Guides', to: '/Account/Guides' }, + { from: '/Guides/signup', to: '/Account/Guides/signup' }, + { from: '/Guides/install-connector', to: '/Account/Guides/install-connector' }, + { from: '/Guides/log-in-to-site', to: '/Account/Guides/log-in-to-site' }, + { from: '/Guides/invite-teammate', to: '/Account/Guides/invite-teammate' }, + { from: '/Guides/switch-team', to: '/Account/Guides/switch-team' }, + { from: '/Guides/buy-credits', to: '/Account/Guides/buy-credits' }, + { from: '/Guides/auto-reload', to: '/Account/Guides/auto-reload' }, + { from: '/Guides/change-plan', to: '/Account/Guides/change-plan' }, + { from: '/Guides/reset-2fa', to: '/Account/Guides/reset-2fa' }, + { from: '/Guides/regenerate-api-keys', to: '/Account/Guides/regenerate-api-keys' }, + + // Group 4: brief detour through a top-level /Developers/ + // section was reverted. Client SDK is back at /Client/*, + // Connector internals live in /Connector/Developers/*, the + // user-facing Connector secrets doc is back at /Connector/ + // secrets, and the HTTP API page sits under /Account/ + // Developers/. These redirects catch any link that was + // grabbed during that brief window. + { from: '/Developers', to: '/Client/intro' }, + { from: '/Developers/intro', to: '/Client/intro' }, + { from: '/Developers/Client', to: '/Client/intro' }, + { from: '/Developers/Client/intro', to: '/Client/intro' }, + { from: '/Developers/Client/installation', to: '/Client/installation' }, + { from: '/Developers/Client/configuration', to: '/Client/configuration' }, + { from: '/Developers/Client/customization', to: '/Client/customization' }, + { from: '/Developers/Client/dev-faq', to: '/Client/dev-faq' }, + { from: '/Developers/Client/faq', to: '/Client/faq' }, + { from: '/Developers/Client/hooks', to: '/Client/hooks' }, + { from: '/Developers/Client/integration-prompt', to: '/Client/integration-prompt' }, + { from: '/Developers/Client/login-feedback-flow', to: '/Client/login-feedback-flow' }, + { from: '/Developers/Client/security', to: '/Client/security' }, + { from: '/Developers/Client/troubleshooting', to: '/Client/troubleshooting' }, + { from: '/Developers/Client/usage', to: '/Client/usage' }, + { from: '/Developers/Client/namespacing', to: '/Client/namespacing' }, + { from: '/Developers/Client/namespacing/css-namespacing', to: '/Client/namespacing/css-namespacing' }, + { from: '/Developers/Client/namespacing/merging-into-existing-composer', to: '/Client/namespacing/merging-into-existing-composer' }, + { from: '/Developers/Client/namespacing/php-scoper', to: '/Client/namespacing/php-scoper' }, + { from: '/Developers/Client/namespacing/strauss', to: '/Client/namespacing/strauss' }, + { from: '/Developers/Connector-development', to: '/Connector/Developers/development' }, + { from: '/Developers/Connector-hooks', to: '/Connector/Developers/hooks' }, + // /Developers/Connector-encrypted-messages used to redirect to + // /Connector/Developers/encrypted-messages, but the page has + // been pulled — Encrypted Messages was documented before it + // shipped. Land on /Connector/intro instead. + { from: '/Developers/Connector-encrypted-messages', to: '/Connector/intro' }, + { from: '/Connector/encrypted-messages', to: '/Connector/intro' }, + { from: '/Developers/Connector-envelope-signature-verification', to: '/Connector/Developers/envelope-signature-verification' }, + { from: '/Developers/Connector-running-behind-a-proxy', to: '/Connector/Developers/running-behind-a-proxy' }, + { from: '/Developers/Connector-secrets', to: '/Connector/secrets' }, + { from: '/Developers/http-api', to: '/Account/Developers/http-api' }, + { from: '/api-reference', to: '/Account/Developers/http-api' }, ], }, ], @@ -100,8 +186,9 @@ const config = { srcDark: 'img/TrustedLogin-Horizontal-White.svg', }, items: [ - // Guides nav item removed — content kept on disk at docs/Guides/ - // but not surfaced until the section is ready to ship. + // Guides surface inside the Account section now (autogenerated + // sidebar picks up docs/Account/Guides/), so no separate navbar + // item — readers reach them via the Account dropdown. { type: 'doc', label: 'Client SDK', @@ -115,8 +202,8 @@ const config = { }, { type: 'doc', - label: 'TrustedLogin SaaS', - docId: 'SaaS/intro', + label: 'Account', + docId: 'Account/intro', }, ], }, @@ -135,8 +222,8 @@ const config = { to: '/docs/Connector/intro', }, { - label: 'SaaS Application', - to: '/docs/SaaS/intro', + label: 'Account', + to: '/docs/Account/intro', }, ], }, diff --git a/sidebars.js b/sidebars.js index bae9c4a..a393519 100644 --- a/sidebars.js +++ b/sidebars.js @@ -31,10 +31,8 @@ const sidebars = { id: 'for-ai-tools', }, ], - // Guides sidebar removed — content kept on disk at docs/Guides/ but - // not surfaced in nav until the section is ready to ship. To restore, - // add the Guides block back here and the matching navbar item in - // docusaurus.config.js. + // Guides moved into the Account section as Account/Guides/. They + // surface automatically through Account's autogenerated sidebar. Client: [ { type: 'html', @@ -42,7 +40,7 @@ const sidebars = { defaultStyle: true, className: 'sidebar-header' }, - { + { type: 'autogenerated', dirName: 'Client', } @@ -59,16 +57,16 @@ const sidebars = { dirName: 'Connector' } ], - SaaS: [ + Account: [ { type: 'html', - value: '

The TrustedLogin Application

', + value: '

Account

', defaultStyle: true, className: 'sidebar-header' }, { type: 'autogenerated', - dirName: 'SaaS' + dirName: 'Account' } ], }; diff --git a/static/img/guides/auto-reload/01-start-from-your-billing-page.png b/static/img/guides/auto-reload/01-start-from-your-billing-page.png index 0dc9a04..3498182 100644 Binary files a/static/img/guides/auto-reload/01-start-from-your-billing-page.png and b/static/img/guides/auto-reload/01-start-from-your-billing-page.png differ diff --git a/static/img/guides/auto-reload/02-open-auto-reload-settings.png b/static/img/guides/auto-reload/02-open-auto-reload-settings.png index 66305fb..567100e 100644 Binary files a/static/img/guides/auto-reload/02-open-auto-reload-settings.png and b/static/img/guides/auto-reload/02-open-auto-reload-settings.png differ diff --git a/static/img/guides/auto-reload/03-choose-when-to-reload-and-how-much.png b/static/img/guides/auto-reload/03-choose-when-to-reload-and-how-much.png index 66305fb..5c14e62 100644 Binary files a/static/img/guides/auto-reload/03-choose-when-to-reload-and-how-much.png and b/static/img/guides/auto-reload/03-choose-when-to-reload-and-how-much.png differ diff --git a/static/img/guides/auto-reload/04-what-happens-on-a-reload.png b/static/img/guides/auto-reload/04-what-happens-on-a-reload.png index 66305fb..9896081 100644 Binary files a/static/img/guides/auto-reload/04-what-happens-on-a-reload.png and b/static/img/guides/auto-reload/04-what-happens-on-a-reload.png differ diff --git a/static/img/guides/auto-reload/05-turn-auto-reload-off-whenever-you-want.png b/static/img/guides/auto-reload/05-turn-auto-reload-off-whenever-you-want.png index 66305fb..5f055a7 100644 Binary files a/static/img/guides/auto-reload/05-turn-auto-reload-off-whenever-you-want.png and b/static/img/guides/auto-reload/05-turn-auto-reload-off-whenever-you-want.png differ diff --git a/static/img/guides/buy-credits/01-open-your-billing-page.png b/static/img/guides/buy-credits/01-open-your-billing-page.png index 0dc9a04..2a74252 100644 Binary files a/static/img/guides/buy-credits/01-open-your-billing-page.png and b/static/img/guides/buy-credits/01-open-your-billing-page.png differ diff --git a/static/img/guides/buy-credits/02-pick-a-bundle.png b/static/img/guides/buy-credits/02-pick-a-bundle.png index d8c0db4..1feae36 100644 Binary files a/static/img/guides/buy-credits/02-pick-a-bundle.png and b/static/img/guides/buy-credits/02-pick-a-bundle.png differ diff --git a/static/img/guides/buy-credits/03-bigger-bundles-carry-bonus-credits.png b/static/img/guides/buy-credits/03-bigger-bundles-carry-bonus-credits.png new file mode 100644 index 0000000..ca20cc9 Binary files /dev/null and b/static/img/guides/buy-credits/03-bigger-bundles-carry-bonus-credits.png differ diff --git a/static/img/guides/buy-credits/04-buy-for-x-stripe-checkout.png b/static/img/guides/buy-credits/04-buy-for-x-stripe-checkout.png index 5679d7d..dfdfbbe 100644 Binary files a/static/img/guides/buy-credits/04-buy-for-x-stripe-checkout.png and b/static/img/guides/buy-credits/04-buy-for-x-stripe-checkout.png differ diff --git a/static/img/guides/buy-credits/05-confirm-the-credits-landed.png b/static/img/guides/buy-credits/05-confirm-the-credits-landed.png index 0dc9a04..49c7be6 100644 Binary files a/static/img/guides/buy-credits/05-confirm-the-credits-landed.png and b/static/img/guides/buy-credits/05-confirm-the-credits-landed.png differ diff --git a/static/img/guides/change-plan/01-open-your-billing-page.png b/static/img/guides/change-plan/01-open-your-billing-page.png index 0dc9a04..3498182 100644 Binary files a/static/img/guides/change-plan/01-open-your-billing-page.png and b/static/img/guides/change-plan/01-open-your-billing-page.png differ diff --git a/static/img/guides/change-plan/02-understand-what-your-current-plan-covers.png b/static/img/guides/change-plan/02-understand-what-your-current-plan-covers.png index 0dc9a04..d43b326 100644 Binary files a/static/img/guides/change-plan/02-understand-what-your-current-plan-covers.png and b/static/img/guides/change-plan/02-understand-what-your-current-plan-covers.png differ diff --git a/static/img/guides/change-plan/03-click-billing-portal-to-manage-your-subscription.png b/static/img/guides/change-plan/03-click-billing-portal-to-manage-your-subscription.png new file mode 100644 index 0000000..5f5e762 Binary files /dev/null and b/static/img/guides/change-plan/03-click-billing-portal-to-manage-your-subscription.png differ diff --git a/static/img/guides/change-plan/03-click-manage-subscription.png b/static/img/guides/change-plan/03-click-manage-subscription.png deleted file mode 100644 index 0dc9a04..0000000 Binary files a/static/img/guides/change-plan/03-click-manage-subscription.png and /dev/null differ diff --git a/static/img/guides/change-plan/04-change-plan-update-card-or-cancel.png b/static/img/guides/change-plan/04-change-plan-update-card-or-cancel.png deleted file mode 100644 index 0dc9a04..0000000 Binary files a/static/img/guides/change-plan/04-change-plan-update-card-or-cancel.png and /dev/null differ diff --git a/static/img/guides/change-plan/04-or-switch-plans-in-the-picker.png b/static/img/guides/change-plan/04-or-switch-plans-in-the-picker.png new file mode 100644 index 0000000..959dedf Binary files /dev/null and b/static/img/guides/change-plan/04-or-switch-plans-in-the-picker.png differ diff --git a/static/img/guides/change-plan/05-your-credits-stay-yours.png b/static/img/guides/change-plan/05-your-credits-stay-yours.png index 0dc9a04..f4683f8 100644 Binary files a/static/img/guides/change-plan/05-your-credits-stay-yours.png and b/static/img/guides/change-plan/05-your-credits-stay-yours.png differ diff --git a/static/img/guides/install-connector/01-open-team-settings.png b/static/img/guides/install-connector/01-open-team-settings.png deleted file mode 100644 index 6c41e10..0000000 Binary files a/static/img/guides/install-connector/01-open-team-settings.png and /dev/null differ diff --git a/static/img/guides/install-connector/01-open-your-team-settings-on-trustedlogin.png b/static/img/guides/install-connector/01-open-your-team-settings-on-trustedlogin.png new file mode 100644 index 0000000..78b2069 Binary files /dev/null and b/static/img/guides/install-connector/01-open-your-team-settings-on-trustedlogin.png differ diff --git a/static/img/guides/install-connector/02-fill-in-your-team-identity.png b/static/img/guides/install-connector/02-fill-in-your-team-identity.png new file mode 100644 index 0000000..d0b2356 Binary files /dev/null and b/static/img/guides/install-connector/02-fill-in-your-team-identity.png differ diff --git a/static/img/guides/install-connector/02-team-identity.png b/static/img/guides/install-connector/02-team-identity.png deleted file mode 100644 index db8c4af..0000000 Binary files a/static/img/guides/install-connector/02-team-identity.png and /dev/null differ diff --git a/static/img/guides/install-connector/03-api-credentials.png b/static/img/guides/install-connector/03-api-credentials.png deleted file mode 100644 index 203637c..0000000 Binary files a/static/img/guides/install-connector/03-api-credentials.png and /dev/null differ diff --git a/static/img/guides/install-connector/03-copy-your-api-credentials.png b/static/img/guides/install-connector/03-copy-your-api-credentials.png new file mode 100644 index 0000000..e260050 Binary files /dev/null and b/static/img/guides/install-connector/03-copy-your-api-credentials.png differ diff --git a/static/img/guides/install-connector/04-onboarding.png b/static/img/guides/install-connector/04-onboarding.png deleted file mode 100644 index fb5e878..0000000 Binary files a/static/img/guides/install-connector/04-onboarding.png and /dev/null differ diff --git a/static/img/guides/install-connector/04-open-trustedlogin-in-your-wordpress-admin.png b/static/img/guides/install-connector/04-open-trustedlogin-in-your-wordpress-admin.png new file mode 100644 index 0000000..84ef3c8 Binary files /dev/null and b/static/img/guides/install-connector/04-open-trustedlogin-in-your-wordpress-admin.png differ diff --git a/static/img/guides/install-connector/05-paste-credentials.png b/static/img/guides/install-connector/05-paste-credentials.png deleted file mode 100644 index 634f71f..0000000 Binary files a/static/img/guides/install-connector/05-paste-credentials.png and /dev/null differ diff --git a/static/img/guides/install-connector/05-paste-your-account-id-public-key-and-private-key.png b/static/img/guides/install-connector/05-paste-your-account-id-public-key-and-private-key.png new file mode 100644 index 0000000..ed48299 Binary files /dev/null and b/static/img/guides/install-connector/05-paste-your-account-id-public-key-and-private-key.png differ diff --git a/static/img/guides/install-connector/06-pick-the-support-roles-that-can-use-trustedlogin.png b/static/img/guides/install-connector/06-pick-the-support-roles-that-can-use-trustedlogin.png new file mode 100644 index 0000000..ed48299 Binary files /dev/null and b/static/img/guides/install-connector/06-pick-the-support-roles-that-can-use-trustedlogin.png differ diff --git a/static/img/guides/install-connector/06-support-roles.png b/static/img/guides/install-connector/06-support-roles.png deleted file mode 100644 index 2a546be..0000000 Binary files a/static/img/guides/install-connector/06-support-roles.png and /dev/null differ diff --git a/static/img/guides/install-connector/07-click-continue.png b/static/img/guides/install-connector/07-click-continue.png deleted file mode 100644 index 57d2c9c..0000000 Binary files a/static/img/guides/install-connector/07-click-continue.png and /dev/null differ diff --git a/static/img/guides/install-connector/07-confirm-the-connection.png b/static/img/guides/install-connector/07-confirm-the-connection.png new file mode 100644 index 0000000..e7071cc Binary files /dev/null and b/static/img/guides/install-connector/07-confirm-the-connection.png differ diff --git a/static/img/guides/install-connector/08-dashboard.png b/static/img/guides/install-connector/08-dashboard.png deleted file mode 100644 index 5272b43..0000000 Binary files a/static/img/guides/install-connector/08-dashboard.png and /dev/null differ diff --git a/static/img/guides/install-connector/08-you-re-done-head-back-to-your-dashboard.png b/static/img/guides/install-connector/08-you-re-done-head-back-to-your-dashboard.png new file mode 100644 index 0000000..1294400 Binary files /dev/null and b/static/img/guides/install-connector/08-you-re-done-head-back-to-your-dashboard.png differ diff --git a/static/img/guides/invite-teammate/01-open-team-members.png b/static/img/guides/invite-teammate/01-open-team-members.png index 3f1f9d8..4e00f0c 100644 Binary files a/static/img/guides/invite-teammate/01-open-team-members.png and b/static/img/guides/invite-teammate/01-open-team-members.png differ diff --git a/static/img/guides/invite-teammate/02-review-pending-invitations.png b/static/img/guides/invite-teammate/02-review-pending-invitations.png index 6604d0d..b6b7769 100644 Binary files a/static/img/guides/invite-teammate/02-review-pending-invitations.png and b/static/img/guides/invite-teammate/02-review-pending-invitations.png differ diff --git a/static/img/guides/invite-teammate/03-fill-in-the-invitation.png b/static/img/guides/invite-teammate/03-fill-in-the-invitation.png index 13fee2e..03c7802 100644 Binary files a/static/img/guides/invite-teammate/03-fill-in-the-invitation.png and b/static/img/guides/invite-teammate/03-fill-in-the-invitation.png differ diff --git a/static/img/guides/invite-teammate/04-your-teammate-accepts-the-invitation.png b/static/img/guides/invite-teammate/04-your-teammate-accepts-the-invitation.png index 1752b4c..7441d1d 100644 Binary files a/static/img/guides/invite-teammate/04-your-teammate-accepts-the-invitation.png and b/static/img/guides/invite-teammate/04-your-teammate-accepts-the-invitation.png differ diff --git a/static/img/guides/invite-teammate/05-change-a-teammate-s-role-or-remove-them.png b/static/img/guides/invite-teammate/05-change-a-teammate-s-role-or-remove-them.png index ef1f526..582e807 100644 Binary files a/static/img/guides/invite-teammate/05-change-a-teammate-s-role-or-remove-them.png and b/static/img/guides/invite-teammate/05-change-a-teammate-s-role-or-remove-them.png differ diff --git a/static/img/guides/log-in-to-site/01-open-the-sites-list.png b/static/img/guides/log-in-to-site/01-open-the-sites-list.png index 4a8e5c4..837e0cc 100644 Binary files a/static/img/guides/log-in-to-site/01-open-the-sites-list.png and b/static/img/guides/log-in-to-site/01-open-the-sites-list.png differ diff --git a/static/img/guides/log-in-to-site/02-find-the-site-you-want-to-log-into.png b/static/img/guides/log-in-to-site/02-find-the-site-you-want-to-log-into.png index 4a8e5c4..a9e7605 100644 Binary files a/static/img/guides/log-in-to-site/02-find-the-site-you-want-to-log-into.png and b/static/img/guides/log-in-to-site/02-find-the-site-you-want-to-log-into.png differ diff --git a/static/img/guides/log-in-to-site/03-click-edit-to-open-the-site.png b/static/img/guides/log-in-to-site/03-click-edit-to-open-the-site.png new file mode 100644 index 0000000..a00bf5a Binary files /dev/null and b/static/img/guides/log-in-to-site/03-click-edit-to-open-the-site.png differ diff --git a/static/img/guides/log-in-to-site/03-click-log-in.png b/static/img/guides/log-in-to-site/03-click-log-in.png deleted file mode 100644 index 4a8e5c4..0000000 Binary files a/static/img/guides/log-in-to-site/03-click-log-in.png and /dev/null differ diff --git a/static/img/guides/log-in-to-site/04-see-the-full-site-record.png b/static/img/guides/log-in-to-site/04-see-the-full-site-record.png index 2c14e58..c3305f9 100644 Binary files a/static/img/guides/log-in-to-site/04-see-the-full-site-record.png and b/static/img/guides/log-in-to-site/04-see-the-full-site-record.png differ diff --git a/static/img/guides/log-in-to-site/05-click-log-in-to-site.png b/static/img/guides/log-in-to-site/05-click-log-in-to-site.png new file mode 100644 index 0000000..1c3caed Binary files /dev/null and b/static/img/guides/log-in-to-site/05-click-log-in-to-site.png differ diff --git a/static/img/guides/log-in-to-site/05-revoke-access-when-you-are-done.png b/static/img/guides/log-in-to-site/05-revoke-access-when-you-are-done.png deleted file mode 100644 index 87b6cfc..0000000 Binary files a/static/img/guides/log-in-to-site/05-revoke-access-when-you-are-done.png and /dev/null differ diff --git a/static/img/guides/log-in-to-site/06-revoke-access-if-something-feels-wrong.png b/static/img/guides/log-in-to-site/06-revoke-access-if-something-feels-wrong.png new file mode 100644 index 0000000..e972bbc Binary files /dev/null and b/static/img/guides/log-in-to-site/06-revoke-access-if-something-feels-wrong.png differ diff --git a/static/img/guides/regenerate-api-keys/01-open-team-settings.png b/static/img/guides/regenerate-api-keys/01-open-team-settings.png index e8e1398..63c531c 100644 Binary files a/static/img/guides/regenerate-api-keys/01-open-team-settings.png and b/static/img/guides/regenerate-api-keys/01-open-team-settings.png differ diff --git a/static/img/guides/regenerate-api-keys/02-find-the-api-credentials-section.png b/static/img/guides/regenerate-api-keys/02-find-the-api-credentials-section.png index e8e1398..1dc7807 100644 Binary files a/static/img/guides/regenerate-api-keys/02-find-the-api-credentials-section.png and b/static/img/guides/regenerate-api-keys/02-find-the-api-credentials-section.png differ diff --git a/static/img/guides/regenerate-api-keys/03-reveal-the-private-key-only-when-you-need-to-copy-it.png b/static/img/guides/regenerate-api-keys/03-reveal-the-private-key-only-when-you-need-to-copy-it.png new file mode 100644 index 0000000..bd76619 Binary files /dev/null and b/static/img/guides/regenerate-api-keys/03-reveal-the-private-key-only-when-you-need-to-copy-it.png differ diff --git a/static/img/guides/regenerate-api-keys/04-email-the-setup-instructions-to-your-developer-in-one-click.png b/static/img/guides/regenerate-api-keys/04-email-the-setup-instructions-to-your-developer-in-one-click.png new file mode 100644 index 0000000..bf5df5f Binary files /dev/null and b/static/img/guides/regenerate-api-keys/04-email-the-setup-instructions-to-your-developer-in-one-click.png differ diff --git a/static/img/guides/regenerate-api-keys/05-if-something-feels-wrong-pause-or-delete-the-team.png b/static/img/guides/regenerate-api-keys/05-if-something-feels-wrong-pause-or-delete-the-team.png new file mode 100644 index 0000000..ab03a5b Binary files /dev/null and b/static/img/guides/regenerate-api-keys/05-if-something-feels-wrong-pause-or-delete-the-team.png differ diff --git a/static/img/guides/reset-2fa/01-open-your-user-settings.png b/static/img/guides/reset-2fa/01-open-your-user-settings.png index 0de4ceb..514cff4 100644 Binary files a/static/img/guides/reset-2fa/01-open-your-user-settings.png and b/static/img/guides/reset-2fa/01-open-your-user-settings.png differ diff --git a/static/img/guides/reset-2fa/02-check-your-current-two-factor-status.png b/static/img/guides/reset-2fa/02-check-your-current-two-factor-status.png index 13202ea..5189cee 100644 Binary files a/static/img/guides/reset-2fa/02-check-your-current-two-factor-status.png and b/static/img/guides/reset-2fa/02-check-your-current-two-factor-status.png differ diff --git a/static/img/guides/reset-2fa/03-scan-the-qr-code-in-your-new-app.png b/static/img/guides/reset-2fa/03-scan-the-qr-code-in-your-new-app.png index cc7936c..b2cdfbf 100644 Binary files a/static/img/guides/reset-2fa/03-scan-the-qr-code-in-your-new-app.png and b/static/img/guides/reset-2fa/03-scan-the-qr-code-in-your-new-app.png differ diff --git a/static/img/guides/reset-2fa/04-enter-the-current-code-to-confirm.png b/static/img/guides/reset-2fa/04-enter-the-current-code-to-confirm.png index cc7936c..c8cd171 100644 Binary files a/static/img/guides/reset-2fa/04-enter-the-current-code-to-confirm.png and b/static/img/guides/reset-2fa/04-enter-the-current-code-to-confirm.png differ diff --git a/static/img/guides/signup/01-create-your-account.png b/static/img/guides/signup/01-create-your-account.png index c34be66..fed6b5c 100644 Binary files a/static/img/guides/signup/01-create-your-account.png and b/static/img/guides/signup/01-create-your-account.png differ diff --git a/static/img/guides/signup/01-land-on-the-registration-form.png b/static/img/guides/signup/01-land-on-the-registration-form.png deleted file mode 100644 index c34be66..0000000 Binary files a/static/img/guides/signup/01-land-on-the-registration-form.png and /dev/null differ diff --git a/static/img/guides/signup/02-fill-the-form.png b/static/img/guides/signup/02-fill-the-form.png deleted file mode 100644 index aee8823..0000000 Binary files a/static/img/guides/signup/02-fill-the-form.png and /dev/null differ diff --git a/static/img/guides/signup/02-review-your-details-and-register.png b/static/img/guides/signup/02-review-your-details-and-register.png index 6f964ad..144e29b 100644 Binary files a/static/img/guides/signup/02-review-your-details-and-register.png and b/static/img/guides/signup/02-review-your-details-and-register.png differ diff --git a/static/img/guides/signup/04-enable-two-factor-authentication.png b/static/img/guides/signup/04-enable-two-factor-authentication.png new file mode 100644 index 0000000..68688e7 Binary files /dev/null and b/static/img/guides/signup/04-enable-two-factor-authentication.png differ diff --git a/static/img/guides/signup/04-first-dashboard-view.png b/static/img/guides/signup/04-first-dashboard-view.png deleted file mode 100644 index 274c5c7..0000000 Binary files a/static/img/guides/signup/04-first-dashboard-view.png and /dev/null differ diff --git a/static/img/guides/signup/04-welcome-to-your-dashboard.png b/static/img/guides/signup/04-welcome-to-your-dashboard.png deleted file mode 100644 index 7543789..0000000 Binary files a/static/img/guides/signup/04-welcome-to-your-dashboard.png and /dev/null differ diff --git a/static/img/guides/signup/05-open-user-settings.png b/static/img/guides/signup/05-open-user-settings.png deleted file mode 100644 index 6267f61..0000000 Binary files a/static/img/guides/signup/05-open-user-settings.png and /dev/null differ diff --git a/static/img/guides/signup/05-set-up-your-profile.png b/static/img/guides/signup/05-set-up-your-profile.png deleted file mode 100644 index c11d315..0000000 Binary files a/static/img/guides/signup/05-set-up-your-profile.png and /dev/null differ diff --git a/static/img/guides/signup/05-welcome-to-your-dashboard.png b/static/img/guides/signup/05-welcome-to-your-dashboard.png new file mode 100644 index 0000000..d408a4a Binary files /dev/null and b/static/img/guides/signup/05-welcome-to-your-dashboard.png differ diff --git a/static/img/guides/signup/06-enable-two-factor-authentication.png b/static/img/guides/signup/06-enable-two-factor-authentication.png deleted file mode 100644 index e97bbaa..0000000 Binary files a/static/img/guides/signup/06-enable-two-factor-authentication.png and /dev/null differ diff --git a/static/img/guides/signup/06-enroll-two-factor-authentication.png b/static/img/guides/signup/06-enroll-two-factor-authentication.png deleted file mode 100644 index d907686..0000000 Binary files a/static/img/guides/signup/06-enroll-two-factor-authentication.png and /dev/null differ diff --git a/static/img/guides/signup/06-set-up-your-profile.png b/static/img/guides/signup/06-set-up-your-profile.png new file mode 100644 index 0000000..0df38ad Binary files /dev/null and b/static/img/guides/signup/06-set-up-your-profile.png differ diff --git a/static/img/guides/signup/07-explore-billing-credit-bundles.png b/static/img/guides/signup/07-explore-billing-credit-bundles.png deleted file mode 100644 index e2a6809..0000000 Binary files a/static/img/guides/signup/07-explore-billing-credit-bundles.png and /dev/null differ diff --git a/static/img/guides/signup/07-see-your-plan-and-buy-extra-credits.png b/static/img/guides/signup/07-see-your-plan-and-buy-extra-credits.png index 603cb7b..b3f1058 100644 Binary files a/static/img/guides/signup/07-see-your-plan-and-buy-extra-credits.png and b/static/img/guides/signup/07-see-your-plan-and-buy-extra-credits.png differ diff --git a/static/img/guides/switch-team/01-open-your-dashboard.png b/static/img/guides/switch-team/01-open-your-dashboard.png index 686217c..f9522a1 100644 Binary files a/static/img/guides/switch-team/01-open-your-dashboard.png and b/static/img/guides/switch-team/01-open-your-dashboard.png differ diff --git a/static/img/guides/switch-team/02-open-the-team-switcher.png b/static/img/guides/switch-team/02-open-the-team-switcher.png index 6d4893d..686f0ea 100644 Binary files a/static/img/guides/switch-team/02-open-the-team-switcher.png and b/static/img/guides/switch-team/02-open-the-team-switcher.png differ diff --git a/static/img/guides/switch-team/03-click-the-team-you-want.png b/static/img/guides/switch-team/03-click-the-team-you-want.png index 9e84e90..6a35141 100644 Binary files a/static/img/guides/switch-team/03-click-the-team-you-want.png and b/static/img/guides/switch-team/03-click-the-team-you-want.png differ diff --git a/static/img/guides/switch-team/04-you-re-now-on-the-new-team.png b/static/img/guides/switch-team/04-you-re-now-on-the-new-team.png index 975578a..6f18867 100644 Binary files a/static/img/guides/switch-team/04-you-re-now-on-the-new-team.png and b/static/img/guides/switch-team/04-you-re-now-on-the-new-team.png differ diff --git a/static/img/guides/switch-team/05-or-press-k-to-jump.png b/static/img/guides/switch-team/05-or-press-k-to-jump.png index fb6ceec..4f91da6 100644 Binary files a/static/img/guides/switch-team/05-or-press-k-to-jump.png and b/static/img/guides/switch-team/05-or-press-k-to-jump.png differ diff --git a/static/img/vendor/permissions/01-overview.png b/static/img/vendor/permissions/01-overview.png new file mode 100644 index 0000000..9282f17 Binary files /dev/null and b/static/img/vendor/permissions/01-overview.png differ diff --git a/static/img/vendor/permissions/02-admin-row.png b/static/img/vendor/permissions/02-admin-row.png new file mode 100644 index 0000000..5fbb68b Binary files /dev/null and b/static/img/vendor/permissions/02-admin-row.png differ