PATRICK STREULE | ARCHITECT | ATLASSIAN | @PSTREULE
Forge: Under the Hood
Recap:
Why
Managed
Auth
Isolation
Model
AWS
Lambda
Agenda
Looking
Ahead
TODAY: CONNECT
Atlassian Infrastructure App Infrastructure
☁ Internet
App
TODAY: CONNECT
Atlassian Infrastructure App Infrastructure
HTTP/Routes
JWTAuth
BusinessLogic
☁ Internet
Data
Data storage
policiesEgress rules
Variable latency
PAAS
Atlassian Infrastructure App Infrastructure
HTTP/Routes
JWTAuth
BusinessLogic
Data
Data storage
policies
Egress rules
Predictable latency
PAAS
Atlassian Infrastructure App Infrastructure
HTTP/Routes
JWTAuth
BusinessLogic
Data
FAAS / SERVERLESS
Atlassian Infrastructure App Infrastructure
InvocationService
BusinessLogic
Data
Business Logic
Runtime
Other
The secret
sauce
InvocationService
Recap:
Why
Managed
Auth
Isolation
Model
AWS
Lambda
Agenda
Looking
Ahead
MANAGED AUTH
const watchersResponse = await api
.asUser()
.requestJira(`/rest/api/3/issue/${issue.key}/watchers`);
const watchersResponse = await api
.asApp()
.requestJira(`/rest/api/3/issue/${issue.key}/watchers`);
Better security
Long-lived secrets are kept within Atlassian
infrastructure, unaccessible from the outside
Manageable for end users
Users can see and revoke all their grants on the
Atlassian Account profile page.
Easier to use
No need to deal with OAuth2 flows or secure
credential and token storage.
Managed
Auth Goals
Better security
Long-lived secrets are kept within Atlassian
infrastructure, unaccessible from the outside
Manageable for end users
Users can see and revoke all their grants on the
Atlassian Account profile page.
Easier to use
No need to deal with OAuth2 flows or secure
credential and token storage.
Managed
Auth Goals
Better security
Long-lived secrets are kept within Atlassian
infrastructure, unaccessible from the outside
Manageable for end users
Users can see and revoke all their grants on the
Atlassian Account profile page.
Easier to use
No need to deal with OAuth2 flows or secure
credential and token storage.
Managed
Auth Goals
UNDER THE HOOD
const rest = await api
.asUser()
.requestJira(`/rest…`);
Runtime
InvocationService
{
  "issue": {
    "key": "ATL-2019"
  },
  "context": {
    "cloudId": "1a5dab50-7544-…f310",
    "accountId": "12345:3b341d…c546"
  }
}
Managed

Auth
{
  "tokens": [
    {
      "accountId": "12345:3b341d…c546",
      "token": "<secret>",
      "service": "api.atlassian.com"
    }
  ]
}
fetch
GET https://api.atlassian.com
/ex/jira/{cloudId}/rest/api/3/..
Authorization: Bearer {token}
PROMPT CONSENT FLOW
const rest = await api
.asUser()
.requestJira(`/rest…`);
Runtime
InvocationService
fetch
Auth Error
  <ThreeLOPrompt
    authUrl={authInfo.url}
    message="..."
    promptText="Authorize"
  />
AUTHORIZE
Authorization UI
CONSENT FLOW (OAUTH2)
Frontend/PopupWindow
Managed

Auth
Start
Authorize URL
Authorization Token
Authorization Token
Consent Screen
auth.atlassian.com
Accept
Login
Authorization Token
Refresh & Access Token
AFTER CONSENT FLOW
const rest = await api
.asUser()
.requestJira(`/rest…`);
Runtime
InvocationService
{
  "issue": {
    "key": "ATL-2019"
  },
  "context": {
    "cloudId": "1a5dab50-7544-…f310",
    "accountId": "12345:3b341d…c546"
  }
}
Managed

Auth
{
  "tokens": [
    {
      "accountId": "12345:3b341d…c546",
      "token": "<access token>",
      "service": "api.atlassian.com"
    }
  ]
}
fetch
GET https://api.atlassian.com
/ex/jira/{cloudId}/rest/api/3/..
Authorization: Bearer {token}
const rest = await api
.asUser()
.requestJira(`/rest…`);
Runtime
Recap:
Why
Managed
Auth
Isolation
Model
AWS
Lambda
Agenda
Looking
Ahead
WITHOUT REQUEST ISOLATION
Time
{
 "issue": {
  "summary":"Unveil X"
 }
}
"Unveil X"
let content = '';
export function demo(event) {
  if (!content) { content = event.issue.summary; }
  return content;
}
{
 "issue": {
  “summary":"Bug in Y"
 }
}
"Unveil X"
{
 "issue": {
  "summary":"As a dev"
 }
}
"Unveil X"
🤭😬😳
CODE BREAKOUTS
const rest = await api
.asRequestUser()
.fetch(`/rest/api/…`);
RUNNING THIRD-PARTY CODE SECURELY
REUSING BROWSER TECHNOLOGY
NodeJS: V8
const rest = await api
.asUser()
.requestJira(`/rest…`);
Fetch implementation
Isolates
The technology behind
iframes in Chrome
No shared resources
Marshalling of data across
isolate boundaries
LIKE CONNECT’S AC-JS
AP.request('/rest/api/…', {
  success: (resp) => {
  }
});
AP host implementation
iframe
“postMessage”
Bridge
Browser
APPLICATION-LEVEL ISOLATION
const doc = await api
.fetch(`https://docs.google.com/document/...`);
const mail = await api
.fetch(`https://mail.google.com/...`);
Fetch
manifest.yml
https://docs.google.com/**
Egress config
URL patterns of hosts
that may be contacted
APPLICATION-LEVEL ISOLATION
import * as fs from 'fs';
const users = fs.readFileSync('/etc/passwd');
FileSystem
it api
://docs.google.com/document/...`);
ait api
://mail.google.com/...`);
manifest.yml
https://docs.google.com/**
REQUEST ISOLATION: SNAPSHOTS
Isolate from Code V8
Memory
Snapshot
01
11001
01011
Isolate from
Snapshot
01
11001
01011
X00 ms X ms
WITH REQUEST ISOLATION
Time
{
 "issue": {
  "summary":"Unveil X"
 }
}
"Unveil X"
let content = '';
export function demo(event) {
  if (!content) { content = event.issue.summary; }
  return content;
}
{
 "issue": {
  “summary":"Bug in Y"
 }
}
"Bug in Y"
{
 "issue": {
  "summary":"As a dev"
 }
}
"As a dev"
DOWNSIDE: NONSTANDARD ENVIRONMENT
Forge
API
JavaScript
Core
NodeJS
API
Browser
API
api.*
Your
Code
Approximate
NodeJS API to
support npm
packages.
Approximate
ServiceWorker
API
CDN: CLOUDFLARE, FLY FORGE
Recap:
Why
Managed
Auth
Isolation
Model
AWS
Lambda
Agenda
Looking
Ahead
Isolation
again :)
AWS LAMBDA: ISOLATION CONT’D
Your Code
Forge Runtime
Sandbox
Guest OS
Hypervisor
Host OS
Hardware
Isolates
cgroups, namespaces, seccomp
Firecracker virtualization
EC2 Bare Metal
AWS LAMBDA: MULTIPLE ACCOUNTS
ManagedAuth…
Forge AWS Accounts
ServiceAWSAccounts
Deploy
Deploy
Invoke
Deployment
Service
Invocation
Service
api.atlassian.com Public API Calls
AWS API Calls (assumeRole)
Latency
AWS LAMBDA: COLD START LATENCY
5-10s 0s
Worker
Local
NAT
ENI
Worker
Local
NAT
ENI
Worker
Remote
NAT
ENI
Worker
LATENCY: SINGLE APP DEPLOYMENTS
modules:
  function:
    - key: main
      handler: index.run
    - key: other
      handler: other.run
LATENCY: LAMBDA PER APP
mainother
Invoke
Invoke
vs.
Invoke“main”
Recap:
Why
Managed
Auth
Isolation
Model
AWS
Lambda
Agenda
Looking
Ahead
Your
App
Your customer
250ms
US Realm
EU Realm
Your
App
Your
customer
We have devoted significant resources
towards ensuring our cloud products are
built and designed in accordance with
widely accepted standards and
certifications.
https://www.atlassian.com/trust/privacy/gdpr
Data storage for apps today
Define Data
Model
Taking multi-tenancy
into account
Implement API
For data retrieval and
modification
Handle
Operations
Backups, Migration,
Capacity planning,
DB upgrades, …
Trust &
Compliance
GDPR, SOC2, 

Data Residency,
Encryption@rest
Isn’t this solved by Entity
Properties?
Yes, but …
ENTITY PROPERTIES ACROSS PRODUCTS
Issue
Project
User
Board
Workflow
Page
Comment
Blog
Space
User
Repository
PR
User
Team
Build
D D
D
GENERALIZED MODEL
ORGANIZATION
SITE USER
UGC: Data Retention, Residency and Encryption PD/PII: GDPR
D
D
CONTAINER
OBJECT
Data deletion
Data is deleted with when its parent chain is
deleted.
Data encryption
Data is encrypted with the same key as its
parent.
Data movement
Moving to another realm, container or
organization, whenever its parent moves.
Data follows
its parent
Data deletion
Data is deleted with when its parent chain is
deleted.
Data encryption
Data is encrypted with the same key as its
parent.
Data movement
Moving to another realm, container or
organization, whenever its parent moves.
Data follows
its parent
Data deletion
Data is deleted with when its parent chain is
deleted.
Data encryption
Data is encrypted with the same key as its
parent.
Data movement
Moving to another realm, container or
organization, whenever its parent moves.
Data follows
its parent
How could a possible Forge
implementation look like?
HYPOTHETICALLY!
DEFINE MODEL
  modules:
    function:
    - key: main
      handler: index.run
    objectTypes:
    - key: Laptop
      properties:
        model:
          type: string
          required: true
          description: The laptop model
        status:
          type: enum
          enum:
          - deployed
          - unassigned
          - ordered
        serialNumber:
          type: string
          required: true
          indexed: true
      relations:
        assignedTo:
          type: User
API
  const mutation = gql`mutation create($input: CreateLaptopInput!)
    createLaptop(input: $input) {
      id
    }
  }`;
  const result = await api.objects.request(mutation, {
    input: {
      model: 'Macbook Pro 2017',
      serialNumber: '000-111-222-333',
      status: "deployed",
      assignedTo: {
        connect: "12345:3b341d11-2ac4-4afe-b429-622b035ac546"
      }
    }
  });
API
  const mutation = gql`mutation create($input: CreateLaptopInput!)
    createLaptop(input: $input) {
      id
    }
  }`;
  const result = await api.objects.request(mutation, {
    input: {
      model: 'Macbook Pro 2017',
      serialNumber: '000-111-222-333',
      status: "deployed",
      assignedTo: {
        connect: "12345:3b341d11-2ac4-4afe-b429-622b035ac546"
      }
    }
  });D
USER
API
  query byUser {
    user(id: "12345:e5c90516-1fb2-11e9-9fb7-df60171038c6") {
      laptop {
        nodes {
          id
          serialNumber,
          model
          warranty
        }
      }
    }
  }
  query expiredWarranty {
    laptops(where:{warranty:{eq:false}}) {
      nodes {
        model
        serialNumber
        warranty
        assignedTo {
          id
          name
        }
      }
    }
TO SUMMARIZE
Atlassian Infrastructure
InvocationService
BusinessLogic
Data
Data storage
policies
Egress rules
Predictable latency
Convenience APIs
FAAS
RUNTIME
RUNTIME / ISOLATES
Thank you!
PATRICK STREULE | ARCHITECT | ATLASSIAN | @PSTREULE

Forge: Under the Hood