Never-Suspend Patterns: Hostnames and Paths, No Regex
A pattern is either a bare hostname or a hostname with a path. `mail.google.com` matches that host and its subdomains. `example.com/docs` also requires the path to start with `/docs`. There are no wildcards and no regular expressions.
Why it matters
The never-suspend list only protects tabs if you enter patterns it can actually match. A bad pattern catches nothing; a confusing pattern might protect the wrong site. Because there's no regex, the rules are simple and fast, but you need to know what "simple" means here.
How it works
A pattern has two parts: a hostname and an optional path. Nothing else.
Hostnames match hierarchically. mail.google.com matches that exact hostname and any subdomain of it: mail.google.com, a.mail.google.com, b.a.mail.google.com. It does not match google.com or notmail.google.com (a subdomain must start after a dot).
This is why google.com alone covers the whole site and all its subdomains (mail.google.com, drive.google.com, etc.), but mail.google.com does not cover Google's other services.
A malicious site can't fake this. evilmail.google.com.attacker.io looks like it ends with .google.com, but the hostname is actually evilmail.google.com.attacker.io and doesn't match.
Paths match by prefix. example.com/docs protects /docs/guide, /docs/api, and /docs/api/reference, but not /blog or /docsearch. The path must start with exactly the characters you wrote.
Case matters. /Docs and /docs are different paths.
Scalpel drops what it doesn't need. If you paste https://example.com/docs/, Scalpel normalises it to example.com/docs. It strips the scheme, the port, trailing slashes, and any fragment. It does not accept example.com/* or *.example.com or regular expressions like /docs.*.
What does not matter
You don't need to list every subdomain. google.com covers mail.google.com, drive.google.com, and ten thousand others in one entry.
Wildcards are not supported, and that's fine. If you want to protect all of Wikipedia except en.wikipedia.org/wiki/Main_Page, you can't do that with a pattern. But that's an edge case. Add wikipedia.org and let it cover everything; if you later realise one page should suspend, add that site to a different browser profile or turn off the protection for that one tab by hand.
The extension doesn't care whether a site is on your list because you pinned it, because you use it constantly, or because you're doing a live demo. Once it's on the list, it's protected.
Code example
Here's what Scalpel sees when you paste patterns:
// Input patterns from the import/export text box
const userPatterns = [
'https://mail.google.com/',
'example.com/docs',
'localhost:3000',
'api.github.com/user'
];
// Scalpel normalises each one
const normalised = userPatterns.map(p => {
try {
const url = new URL(p.startsWith('http') ? p : 'http://' + p);
return url.hostname + (url.pathname === '/' ? '' : url.pathname);
} catch {
return null; // invalid
}
});
// Result: ["mail.google.com", "example.com/docs", "localhost:3000", "api.github.com/user"]
// On each tab activation, Scalpel checks if the tab URL matches any pattern
function isProtected(tabUrl, patterns) {
const url = new URL(tabUrl);
const host = url.hostname;
const path = url.pathname;
for (const pattern of patterns) {
if (!pattern) continue;
const [patternHost, patternPath] = pattern.split('/').length > 1
? [pattern.split('/')[0], '/' + pattern.split('/').slice(1).join('/')]
: [pattern, ''];
// Check hostname: exact match or a subdomain of the pattern
const hostMatches = host === patternHost || host.endsWith('.' + patternHost);
// Check path: prefix match (or no path required)
const pathMatches = !patternPath || path.startsWith(patternPath);
if (hostMatches && pathMatches) {
return true; // Protected
}
}
return false;
}
// Examples
isProtected('https://mail.google.com/inbox', ['mail.google.com']); // true
isProtected('https://a.mail.google.com/inbox', ['mail.google.com']); // true (subdomain)
isProtected('https://example.com/docs/guide', ['example.com/docs']); // true (path matches)
isProtected('https://example.com/blog', ['example.com/docs']); // false (different path)
isProtected('https://api.github.com/user/repos', ['api.github.com/user']); // true
How Scalpel shows it
In the Settings tab, the never-suspend list shows one pattern per line. Paste a URL or type a hostname, and Scalpel normalises it for you. If you add a path, it highlights what that means: "This site and everything under /docs". The import/export button lets you see the full normalised list as plain text, one per line, so you can verify what you entered.