RFC-137-B: Forge App Suspension Behaviour

@JannickJoosten if we decide to do a suspend/unsuspend event we can evaluate to extend to remotes too

I want to throw one issue into this topic that may be related depending on how you (atlassian) will implement this RFC:

When an admin user is logging into another user to check for issues the license state is empty. So depending on the RFC implementation there could be missing triggers / product events for us to process as a result of this RFC

I like #2 - but I would really LOVE #1 IF the standard message had a way to tell the user where to reach out and find the org admin that they want to beg/plead their case with… :slight_smile: That would actually make this a huge feature for us (since we don’t know what people they should ask in a large organization).

Otherwise #2 would be the better thing for us.

I would love to see a combination of #1 and #2, where there is a standardised message, but partners get to add custom messaging through the manifest.

Think custom support URL (that links to the right customer portal for quick access), contact information, etc. This doesn’t have to be multi-line free text, but it would really help if we can differentiate this per app.

@scottohara as mentioned below this is something we are evaluating. There is a 90 grace period after a license expires during which time the app is still active - giving you the time to communicate an upcoming suspension to users, rather than having the platform handle it for you

Hi Julia,

As far as I am aware, the app itself has no visibility into the grace period (it is not available in the context payload for functions, nor is it available in the Forge License API).

Is the “grace period” flag something that the Forge team could surface directly to apps? Fetching this data from the Marketplace API is impossible for RoA apps, and still undesirable for apps with Forge Remote.

I may be misunderstanding this, but I had interpreted @JuliaDaehne ’s comment as meaning:

  • context.license.isActive will be false on the date the license term ends
  • app becomes “suspended” 90 days after that

This would mean that any message we currently show for inactive licenses continues to show for 90 days, and this RFC is about what happens after 90 days.

If I’m mistaken and that isn’t the case, then I have the same question as @scott.dudley .

Ditto. Clarity would be good. Our earlier comments were based on assumption that on the expiry date, the app would become suspended. There was no mention of a grace period in the RFC text. Just the following that implied (to me) that it happens immediately.

Showing a placeholder UX explaining the suspension state is critical to prevent unnecessary support tickets. Also, triggering the unsuspended lifecycle event with precise timestamps is essential for our reconciliation logic to safely sync without breaching API rate limits.

in general, remotes need lifecycle events and this is a lifecycle event. There are cases (e.g., syncing data with other sources) where it’d be far better for the remote to know to ignore in-bound events from other systems, or stop internal periodic processing. Otherwise the remote does useless work and generates errors attempting to interact with an Atlassian host app.

With https://developer.atlassian.com/changelog/#CHANGE-3260 being able to filter out apps without license is great. It might be nice to also be able to implement a little bit of leeway if the customer comes back after a month. But at some point we would also go forward an cut off unlicensed apps to reduce costs for Forge.

@JuliaDaehne Appreciate the RFC. Improvements to the app lifecycle can be really rewarding for the whole community.
Have you also considered updating app installation behaviors in regards to this change?
Right now, apps installation require an active license - with the known very generic error messages. If licensing does not work (e.g. no CC), then the installation simply fails and nobody knows why.

This could be an opportunity to allow installations into an unlicensed, suspended behavior. Decouple installation of the app from the licensing. This would make it a lot easier for us partners to support these customers.

Or perhaps just fix the error message on installation to say you need a credit card registered to install marketplace apps. I’ve lost count of the number of customers we’ve had to send to the community page for this well known multi-year bug.

You are spot on @scottohara - the proposal is to let partners handle the UI message for 90 days and suspend the app as per RFC proposal thereafter. This is proposed in response to partner feedback on negative customer impact if we were to suspend app without giving partners the option to communicate upcoming suspension in advance

Hi @andreas.schmidt app installation behaviour is not part of this work. Have you raised a FRGE ticket for this?

Hey @JuliaDaehne,

are there any plans to extend https://developer.atlassian.com/changelog/#CHANGE-3260 to workflow post-functions too?

Hi Julia,

Can Atlassian work to solidify the mechanism by which vendors can actually do this? The original RFC mentions the following:

There are a few shortcomings here:

Additional latency for displaying expiration messages

To present a warning message to users, apps need to present this information across all appropriate UI surfaces.

Alerting of expiration was previously possible without any overhead by using the addon_is_licensed condition (in both Connect and Forge). This proposal now forces an additional roundtrip API call onto the critical path for rendering all API surfaces, which is inefficient from a user rendering time perspective, and which increases the vendor’s operational cost (the docs suggest that the API cannot be accessed from the front end, so everything presumably has to happen from the back end).

License API rate-limiting yields a non-resilient system

The license API has an unhelpful level of rate-limiting applied (one call per five minutes), which makes the API not particularly resilient. This creates problems for apps in the case of transient errors (if I fetch the data and try to cache it on my side, but my app experiences a transient error writing to its remote dB, or to KVS, then I cannot try again for five minutes).

License API is missing grace period information

The Forge license API does not include any information about the grace period. RoA apps cannot call MPAC APIs to retrieve this data, and even for non-RoA apps, having to call an external system is undesirable.

Fidelity of grace period data

Even if the grace period information is provided, if Forge will be drawing on the same data source as MPAC, the fidelity of this data is fuzzy and unreliable. To give some examples:

a) When a paid subscription enters a grace period, the “inGracePeriod” flag is set to Yes, but the maintenance end date is also extended by 90 days. This part is OK and expected.

b) If the subscription is subsequently renewed, the maintenance end date is sometimes increased (to correspond to the renewed subscription) but the change to data is applied a couple of hours before the “inGracePeriod” flag is removed. If apps are going to use this data for direct customer-facing messaging, these updates need to be atomic and reliable. No customer should see a message saying the “app is in a grace period and it will expire in +1.5 years” when the admin has actually renewed the app for +1.5 years.

c) Weird things regularly happen to licenses during grace periods. For example, a commercial license that exceeds its grace period will transition from active to cancelled, and later, suddenly transition from a commercial license to an evaluation license. The same license can then transition from cancelled to active, inGracePeriod from Yes to No, from evaluation back to commercial, then active back to inactive, then inGracePeriod from No to Yes (and all of this within a period of 48 hours).

If apps are intended to display user-facing messaging, they need absolutely reliable data on:

i. Is the app actually in a grace period or not? When does it actually expire? This data needs to move in lockstep with the other fields.

ii. Is the app truly a commercial license that has expired, or is it an expired evaluation license that is not associated with a recently-paying customer? Having paid licenses transform themselves into “evaluation” licenses for reporting purposes does not work for these types of user-facing messages, because vendors typically want to distinguish between existing paid customers who churned and evaluators who have never purchased.

Suggestions

In summary, here is what I think would make this workable:

1- Atlassian should provide grace period and maintenance end date data to apps in the Forge context, and as part of the Forge Remote payload. Alternatively, it could provide an in_grace_period condition to allow vendors to decide when to display this information and avoid incurring additional API calls on every render. Having extra API calls for grace period clients is probably OK. Forcing this on all clients is not.

2- Failing that, the Forge License API needs to provide the grace period information. Additionally, the API rate limits need to be considerably loosened so that it can be used regularly by apps. Can Atlassian not come up with a way to make the license API non-expensive on its side, implement its own caching, and then allow vendors to use this API according to the standard rate limits of the rest of the Forge platform?

Forge Cache is dead, so the current implementation forces vendors to make a Forge KVS call for every rendering operation (to see if we have recent cached license data), and if expired, to make a call to the Forge License API, then re-persist the data to KVS. And what about when apps receive multiple concurrent requests to the same tenant? If the KVS data is expired, we make a request to fetch from the license API, but a new request for the same tenant comes in while the first license API request is in flight, presumably already triggering rate limits. This can clearly be handled as an edge case, but everyone has to think about it and test it. Every single vendor would need to manage this complexity, which also incurs extra function and storage cost.

Atlassian seems to be suggesting that the 90-day grace period be used for customer messaging as a best practice for all apps, so actually implementing the strategy should not be this hard.

3- Fix the fidelity of the grace period data so that we can reliably use it to display user-facing communication. Work with the commerce/Marketplace/whatever teams to ensure that we are getting good data, paying particular attention to being able to distinguish between paid users and evaluators, working through all of the edge cases of expired evaluations/paid licenses, the random transitions that sometimes happen (such as when customers lapse but then re-renew or re-evaluate), evaluating the timing of those transitions, and also ensuring the atomic updates of data in whatever data source Forge chooses to provide.

Thanks for the detailed RFC. The per-scenario breakdown is helpful.

A few notes from the Gliffy team:

No concerns about functions, triggers, events, or Forge Storage / Remote going offline for suspended installs, and we wouldn’t need a suspended/unsuspended lifecycle event.

Our one real concern is hiding UI extensions, especially macros. The RFC says these will be “hidden or shown as unavailable”, and which one isn’t specified. If it resolves to hidden, the diagram simply disappears from the page with no explanation, usually in front of an end user rather than the admin who renews. Today when a license lapses our diagrams still render in view mode (editing disabled) and we restrict access via the license API. We’d ask it land on the “shown as unavailable” side: ideally one that still renders the diagram, restricting editing rather than viewing.

Two clarifications:

  • Does trial expiry automatically trigger suspension, or is that separate?
  • How does the suspend flag interact with the existing isActive flag? If suspended installs never invoke our code, isActive checks seem redundant.

Thanks!

@JuliaDaehne, thank you for documenting the different suspension conditions/reasons. However, I wonder if this list is complete. We’ve been getting a Tenant is restricted: suspended-inactivity response from the Jira API, and we don’t understand the meaning of this. Is this covered by one of the categories in your RFC, or is this a separate category that’s not documented yet?

Hi, we need an explicit unsuspended lifecycle event, delivered after storage and product APIs are available, with suspension reason, start/end timestamps, and whether product events were replayed. Without that, apps cannot safely know when to run reconciliation or distinguish data-residency replay from dropped-event recovery.

We also need ideally need a product API/audit mechanism to enumerate content, label, permission, move, delete, archive, and space changes since a timestamp. Current-state reconciliation is not enough for cases where the missed event actor/order matters.

Finally we will need clarity on Forge async queue retention/behaviour during suspension.