Security — Outlier
A security policy for an app with no servers is a short document, and pretending otherwise would be the first thing wrong with it. Outlier runs entirely inside Atlassian's platform. There is no backend, database or network of ours. There is nothing to breach that is not Atlassian's own infrastructure.
This page is about how the app is built and operated. The one source for what is read, stored and kept, and for how long, is the privacy policy. The whole arithmetic is on the method page.
Last reviewed: 2026-10-06.
At a glance
- Your browserthe gadget's interface, in its own iframe on the dashboard.
- A Forge function, inside Atlassianreads Jira as the person looking, or as the app where there is no user to act as.
- Your Jira site and Forge storageit reads Jira and writes only to its own storage on your site.
- Anywhere elseout of reach. The manifest declares no external host, no egress domain, no Forge remote and no web trigger.
| The question | The short answer |
|---|---|
| Where it runs | Inside Atlassian's platform. No backend, database or network of ours |
| What you can check first | The manifest, on the consent screen. Below |
| Where the data lives | Forge storage on your site, and nowhere else. PINNED data residency |
| What is read and kept | The privacy policy is the one source |
| Whose permissions | The person looking, for every read a person or their rule triggers. The app's own reads are four, named |
| What it may ask Jira for | Granular read scopes and storage:app. Why some look wrong |
| What the vendor sees | Application logs: counts, names of fields, the ids of a card or board, and the path of a failed request to Jira. Below |
| What can leave your site | Nothing the app sends. Two features let you send something out |
| Before it ships | Two gates that fail the build, two steps done by hand |
| A vulnerability | support@chmut.com or Atlassian; a critical one fixed within 10 days |
| Who stands behind it | One person. What that means |
What you can verify without trusting us
Most security claims about an app have to be taken on faith, because the source is not public. One does not, and it is the one that matters most.
| What | Where to check it | What it shows |
|---|---|---|
| The Forge manifest | The consent screen | No external host, no egress domain, no Forge remote, no web trigger |
| Its one relaxation | The same manifest | content.styles: unsafe-inline, and nothing that loosens scripts or connects |
| Runs on Atlassian | Atlassian's own badge, on the listing | Atlassian-hosted compute and storage, measured rather than asked |
The manifest
Atlassian shows it to you on the consent screen. It either declares an external host, an egress domain or a Forge remote, or it does not. Outlier declares none of them, and no web trigger. No public endpoint of ours exists to be called.
That is not a promise about our intentions. It is a property the platform enforces, and you can read it before you click install.
The one relaxation
The manifest declares one relaxation, and it is better read here than discovered there: content.styles: unsafe-inline. It permits the inline styles React writes inside the app's own iframe. It does not loosen what scripts may run or what the page may connect to.
Runs on Atlassian
Atlassian's Runs on Atlassian badge certifies a narrower thing than it sounds like. Outlier carries it, under Trust signals on the listing.
- It is awarded by measuring the app, rather than by asking us.
- It means the app uses Atlassian-hosted compute and storage.
- It permits an app to send data out for analytics. This one sends nothing out at all.
Everything else on this page is a claim about work we do. It is written to be specific enough to be argued with.
Where the data lives
Forge storage, and nowhere else.
| Question | Answer |
|---|---|
| Which realm | Your site's. Storage follows it automatically |
| Data residency status | PINNED, without any configuration on your part |
| Shared between customers | No. Storage belongs to your installation; nothing is shared between one site and another |
| Where an invocation runs | Atlassian's to decide. Forge may occasionally run one elsewhere, always inside Atlassian |
Storage residency and compute location are not the same guarantee, and a review will ask. Atlassian's own documentation says Forge may occasionally execute an invocation from a location other than the host product's. So the data resides where your site does, while an invocation may sometimes run elsewhere. Nothing leaves Atlassian, so what could run elsewhere is Atlassian's infrastructure rather than a vendor's.
What is read, and what is kept
What the app reads, what it keeps and for how long is set out once, in the privacy policy: what the app reads and what the app stores. This section lists the controls that hold those facts true.
| Control | What it does |
|---|---|
| One boundary for both reads | The text and the names a changelog carries are dropped in the one function both read paths share, before anything is derived and before anything is written |
| A guard on every write | A field named for a person is refused by name, at any depth |
| Deletion on uninstall | A preUninstall handler deletes the app's own rows, the most revealing first |
Jira's changelog endpoint takes no field filter, so that text and those names do arrive, and are dropped at that boundary. What is used and what is dropped is set out in full in the privacy policy.
The guard reads names, not values
There is no field named for a person anywhere in the schema. Every write is refused by name, at any depth, if one appears. The guard reads field names rather than values. That is why the claim is about what the app asks for, and not only about what the guard catches.
Deletion has a deadline
The preUninstall handler deletes the app's rows rather than leaving them to the twenty-eight days Forge would otherwise hold them. It has a deadline it may not meet.
- On a large installation it deletes the most revealing rows first.
- It records what it could not reach.
- Whatever is left falls under Forge's own twenty-eight-day window.
"We deleted everything" and "we deleted what we could in the time we were given" are different claims. Only the second can be made honestly by a function with a budget.
An administrator has no button for deleting one card's data on its own. What exists is uninstalling, and the app's own forgetting of a board nothing reads. The privacy policy gives both clocks.
Whose eyes the data is read with
Every read of Jira that a person or their Automation rule triggers is made as that person.
| Read | Made as |
|---|---|
| The card, the configuration screens and the admin page | You |
| The Automation step | The person whose rule it is |
| The hourly collection, which has no user to act as | The app |
| The one-off walk of a board's history, when a card is first configured | The app, for the same reason |
| After a board is forgotten, which tickets the boards left still hold | The app, for the same reason |
| The inventory of cards this installation holds | The app. The one exception |
The app reads as itself in four places: the hourly collection, the one-off walk, the check after a board is forgotten, and the inventory of cards. The first three have no user to act as. The fourth has no board to check against, and is the one exception.
Everywhere else, before stored data is shown to anyone, the app asks Jira for the board's configuration as the reader. A refusal becomes a named "you cannot see this board" state rather than a number.
The one exception
There is one exception, and naming it is cheaper than having it found. The inventory of cards this installation holds is read as the app. The question, which cards exist here, has no board to check against.
- A card on a board you cannot open still appears in that list.
- It is listed by its board id, with the day it was last opened. That is the day its dashboard was last opened or a rule last asked for it. Where two such cards would read alike, the filter id or the day it was added tells them apart.
- Its board name, filter name and labels are not shown. The names come from a request made as you, and the labels are withheld to match.
The scopes that look wrong
The app asks for granular scopes rather than the single classic read:jira-work scope. The list is longer to read, and that is the point: the classic scope would grant comments, attachments and worklogs the app never touches.
A few entries look wrong for a product that reads nothing about people: read:user, read:group, read:avatar, read:audit-log. Jira requires each endpoint's scopes as a complete set, and four endpoints between them account for every entry that looks wrong.
| Endpoint | Scopes in its set | The entries that look wrong |
|---|---|---|
| Reading one field of a saved filter | nine | read:user and read:group among them |
| Searching the work in flight | five | read:audit-log, one of them |
| Counting the same search | A different set from the search's | read:field, read:field.default-value and read:field.option, none of which the search itself asks for |
| Asking a board how its columns are arranged | - | read:board-scope.admin, the only scope Atlassian publishes for that endpoint |
- Counting a search is a different endpoint from running it, with a different set.
- There is no reader's variant of
read:board-scope.adminto ask for instead.
The app reads no user, no group, no avatar and no audit log, administers no board, and writes nothing anywhere.
What is checked before anything ships
Two of these are gates that fail the build. Two are steps somebody performs, and they are marked as such. A check somebody remembers to run is not a control, and calling it one would be the same overstatement this page is trying to avoid.
| Check | Kind | Detail |
|---|---|---|
Dependencies are scanned with npm audit on every merge to main and again on every release: everything the gadget installs, and the app's production dependencies. The app's build tools, which never ship, are not scanned | Gate | The build fails at moderate severity or above, and a release that fails it offers no deploy. npm run check runs the same audit first, before every push |
| The manifest's scopes are held to what the app's endpoints accept | Gate | In both directions: an endpoint missing a scope fails, and so does a scope no endpoint needs. The build also holds every read a person triggers to the acting user |
| The vendored scope table is re-checked against Atlassian's published API descriptions | Step | By hand, before scopes are changed, the only time its answer matters |
| The built bundles are read before a release, not only the source | Step | No network call of ours, no markup built from strings, no eval |
The scope check and the acting-user check were both verified by mutation: deliberately breaking them to confirm the build goes red. A green test is not evidence that a test catches anything.
What the vendor can see
Application logs, and nothing else.
| Logs | What that covers |
|---|---|
| Carry | Counts, durations, the shapes of endpoints called and the names of fields |
| Never carry | Ticket content, a query or a person |
| Ids | Ten of the app's thirty-three logging statements print a card's id or its board id |
| A failed request | Logged with the path it asked, ids included |
- A card's id begins with its board id. Nine of the ten report one card's failure or progress; the tenth names the board whose question held up the check after another board was forgotten. That is a deliberate trade: a failure nobody can attribute to a card or a board is a failure nobody can fix.
- The per-endpoint timings have any path segment containing a digit removed, so a ticket key cannot arrive that way.
- A failed request to Jira is the exception. Its message names the path it asked, with the query removed and the ids left in: a board id, a filter id, or the key of a ticket whose history was being paged.
- Logs are retained by Forge on Atlassian's schedule. Nothing is exported and nothing is kept beyond it.
What can leave your site, and who decides
The app sends nothing out of Atlassian. Two features let you send something out, and both are yours to switch on.
| Feature | Who switches it on | Where it goes |
|---|---|---|
| The Automation step | Whoever writes the rule | Wherever that rule sends it, with that rule's credentials |
| Ask Rovo why | Somebody pressing it, where your site has Rovo | Your Rovo, in your instance, on your credits |
The Automation step
The app can answer an Automation rule with this morning's text. What the rule then does with it is the rule's business. If it posts to Slack through a "Send web request" action, that is your rule, your schedule, your channel and your credentials. The app has no destination of its own. It cannot acquire one without a new manifest you would have to consent to.
Ask Rovo why
A button that hands the card's own text to your Rovo, in your instance and on your credits, when somebody presses it; this app deploys no model and calls none. What the question holds and where the answer goes are listed in the privacy policy.
Who can deploy, and what stops them
A Forge deploy reaches every installation with no action by you. That makes a deploy the fastest way to contain an incident, and the deploy credential the most valuable thing to steal.
Every deploy runs in Bitbucket Pipelines, as bitbucket-pipelines.yml in the repository declares it, and only from a release tag.
| What | How |
|---|---|
| A release | A v tag, which the "Tag a release" step cuts from main after the checks there pass. Whatever commit a tag names, the pipeline runs both checks on it again before it offers a deploy |
| Development, and these pages | Deployed from the tag, each by a step somebody starts |
| Production | Only after development and the sites have deployed the same commit. Every production deploy becomes a new version of the Marketplace listing |
| Any branch or pull request | Nothing deploys |
| The deploy credentials | Secured variables in Bitbucket, never in the repository. Each belongs to the deployment that uses it, so only that step can read it |
| The steps that build and test | Hold no credentials at all |
| Rolling back | Redeploy of the previous release, from Bitbucket's deployment history. The step rebuilds from that release's own commit |
What the deploy steps do not run
Each deploy step installs its dependencies with lifecycle scripts disabled, both the app's and the tools'. Running arbitrary install code in the one environment holding a deploy token is the risk that flag exists for.
The Forge CLI and the tool that deploys these pages are pinned to one version each, so a major release cannot change what deploys on the day it ships.
The repository has one account with write access, the vendor's. On this Bitbucket plan a deployment cannot be tied to a branch or a tag by a setting. The pipeline file does that, and only that account can change it.
The vendor's machine keeps an Atlassian token of its own, because reading the app's logs and upgrading an installation need one. It could deploy, but is not used for that.
Reporting a vulnerability
Write to support@chmut.com. Say what you found and how to reproduce it. You do not need a proof of concept for us to take it seriously.
You will get a human reply within 48 hours, on the same working days as any other message to support. That is a commitment about acknowledgement rather than about a fix.
You can also report it to Atlassian. They raise it in their Marketplace Security project, where it arrives with a remediation due date. Either route reaches the same person.
The app is held to these timeframes. They are Atlassian's rather than ours.
| Severity | CVSS | Fixed within |
|---|---|---|
| Critical | 9.0 and above | 10 days |
| High | 7.0 to 8.9 | 4 weeks |
| Medium | 4.0 to 6.9 | 12 weeks |
| Low | below 4.0 | 25 weeks |
If there is an incident
A documented response plan exists. It is kept with the code rather than in somebody's head. Its commitments, in order of the clock:
| When | What happens |
|---|---|
| Within 24 hours of becoming aware | Atlassian is notified, as a P1 |
| At least every six hours, while it is open | Updates go to Atlassian |
| Within 72 hours of identification | Affected customers are notified |
Containment here is unusually direct. A Forge deploy reaches every installation without any action by you. So rolling back to the last good revision is both the fastest response available and a complete one. There is no upgrade for an administrator to apply, and no site left behind on a broken build.
What this page does not claim
A short list, because a security page that only lists strengths is a marketing page.
| This page does not claim | What is true instead |
|---|---|
| A team | Outlier is built and maintained by one person |
| Someone watching | There is no rota, no around-the-clock monitoring and nothing watching the logs for anomalies |
| Review of a change | There is no second pair of eyes on a change |
| Detection | Discovery of a problem would come from a customer, from Atlassian, or from looking |
| A rehearsed incident plan | The incident response plan is written and has never been exercised |
| Certification | The app holds no compliance certifications |
| External testing | It has not been penetration-tested by an external vendor, and does not run a bug bounty programme |
| Code scanning | Dependency scanning is the only automated security scanning in the build. There is no static analysis and no dynamic testing |
| A year of logs | Application logs live on Atlassian's retention schedule rather than a year of our own. That would make a late-discovered incident harder to reconstruct |
None of that is a reason to distrust the app. It is the shape of a small product, and you are entitled to know it before you install one.