Adding MIME Types To Support App Distribution On Apache, IIS, and NGINX

iOS Apps are .ipa files, which are compiled bundles of files that comprise an App on a device. By default, most web servers do have a handler that tells them what to do in the event that a call attempts to access one of these files. Therefore, in order to support downloading those files properly, you need to teach the server how to handle them.

First, let’s grab the MIME type from the Mac file command. To do so, run file with the, big surprise, –mime-type option and then the path to the file:

file --mime-type /Users/ce/Downloads/enrollmentProfile.mobileconfig

The output would be as follows, indicating that a file with the .mobileconfig extension has the application/octet-stream extension:

/Users/ce/Downloads/enrollmentProfile.mobileconfig: application/octet-stream

Since more and more apps are deep linking a plist into the app, we’ll also add a plist. The output on a Mac for the various file types is:

  • .mobileconfig: text/xml or application/octet-stream if signed
  • .mobileprovisioning: text/xml or application/octet-stream if signed
  • .ipa: application/x-ios-app
  • .plist: text/xml or application/octet-stream if a binary plist

In the above, note that a signed mobileconfig, a signed mobile provisioning, and a binary plist are basically interpreted as binary files. This means that when possible, use signed mobileconfig and mobileprovisioning files so you have a consistent handler.

We’ll start defining those with Apache. Handlers are managed in Apache’s global configuration file, often located at /etc/httpd.d/httpd.conf and you would paste the following towards the bottom of the file where you see the media types (note that each AddType is teaching the web server what type of file each file extension indicates):

AddType application/octet-stream .ipa
AddType text/plain .plist
AddType aapplication/octet-stream .mobileconfig

In the above example we set a plist to plain in order to show that, well, sometimes it is. Alternatively (or additively if you need to host both binary and flat plist files), you could create an .htaccess file in the directory with the files (e.g. if you don’t have root access to change the httpd.conf), by adding something similar (the # is indicating a commented line):

# Apps
AddType application/octet-stream .ipa
AddType application/octet-stream .plist
AddType application/octet-stream .mobileconfig

For IIS, go into IIS Manager and right-click on the name of the server, select Properties and click on New… in order to create new MIME types. Then add each using the above types.

To update on nginx, edit the mime.types file in the conf directory for nginx. This is often found in /etc/nginx or /opt/nginx but ymmv. Once found, in mime.types look for a types section wrapped in curly-braces {}:

types { application/octet-stream mobileprovision; application/octet-stream mobileconfig; application/octet-stream plist; application/octet-stream ipa; }

Note: In some cases you might find that “application/x-apple-aspen-config” and in others text/plain or text/xml works better for .mobileconfig MIME types.

If you have failures, you can use a proxy to check it. Here, you’d probably want to use a unique port number to make calls easier to use. If you use Charles Proxy, you’d configure the proxy in the Wifi settings of an iOS device and then open the link in a browser and watch for any failures.

Adding App Notarization For Macs To Your Build Train

Apple sent the following message out to developers yesterday:

Dear Developer, 

We’re working with developers to create a safer Mac user experience through a process where all software, whether distributed on the App Store or outside of it, is signed or notarized by Apple. With the public release of macOS 10.14.5, we require that all developers creating a Developer ID certificate for the first time notarize their apps, and that all new and updated kernel extensions be notarized as well. This will help give users more confidence that the software they download and run, no matter where they get it from, is not malware by showing a more streamlined Gatekeeper interface. In addition, we’ve made the following enhancements to the notarization process.
Legacy code is fully supported, even if it contains unsigned binaries. While new software and updates require proper signatures in order to be notarized, you can upload your existing software as-is.Apps with plugin ecosystems are better supported.Stapler supports all types of bundles and plugins.Xcode 10.2 adds secure timestamps and other code signing options required by the notary service.Related documentation has also been improved. We encourage you to take look at Notarizing Your Apps Before Distribution and Hardened Runtime Entitlements.

If you have any questions, contact us

Best regards,
Apple Developer RelationsTM and © 2019 Apple Inc.
One Apple Park Way, MS 301-1TEV, Cupertino, CA 95014.

All Rights Reserved | Privacy Policy | Account 

If you would prefer not to receive future communications from Apple Developer, you may unsubscribe.

Many organizations have a solution to automate their build process for software and will need to now add submitting an app for notarization to this process. Before you start, there are a few things you should know:

  • This is an automated scan that usually takes about 20 minutes and requires at least the 10.9 macOS SDK.
  • Before submitting, make sure code-signing has been enabled for all executables and that you enabled the Hardened Runtime option.
  • Find a workaround if you’re setting to true for any reason.
  • Make sure to use an Apple Developer ID instead of a local cert from Xcode for apps and kexts. And make sure all code-signing certs have a timestamp when running your distribution workflows in Xcode or if using codesign make sure to add –timestamp.

You can use any tools for the next steps. Because I have a Bamboo setup on my desk, next I’m going to open it and create a command task. To do so:

  • Open the Tasks configuration tab for a job (or default job in a new plan).
  • Click Add Task.
  • Add a Task Description, which is just how the task is described in the Bamboo interface.
  • Uncheck the box to “Disable this task”
  • Provide a path to the command executable, which in this case will be a simple bash script that we’ll call /usr/bambooscripts/ If you’re stringing workflows together you might add other scripts as well (e.g. a per-product script as opposed to a generic script that takes positional parameters for arguments).
  • Provide any necessary Arguments. In this case it’ll just be a simple job but you can reduce the work by adding arguments for processing paths of different products.
  • Provide any necessary Environment Variables. We won’t use any in this project.
  • Provide any necessary “Working Sub Directory” settings, which is an alternative directory rather than using a relative path. If you don’t provide a working sub directory, note that Bamboo looks for build files in the root directory.
  • Click the Save button (as you can see below).

Now we’ll need to use scrub with the altool. Here, we’ll use the –notarize-app option and then define the bundle (using the reverse naming convention you’ve always used for the –primary-bundle-id option and then the username and password from your Apple ID linked to your Developer ID and finally the –file which is the zipped output from Xcode.

#!/bin/bash /usr/bin/xcrun/xcrun altool --notarize-app --primary-bundle-id "com.myorg.myproduct” --username “” --password “icky_passwords“ --file "/Users/krypted/Documents/"

We'll call this script /usr/bambooscripts/ and then let the job pick it up and process it.

Oh funny. I just noticed Rich Trouton posted a writeup on Notarization at I'd read that as well.

Get Your CustomerID From G Suite

There are a few ways to grab your CustomerID from G Suite. This is important when configuring SSO or when interfacing with G Suite programmatically (through their lovely API).

The first and easiest way is to look at the web interface. This isn’t the most intuitive. To find the key, open Google Admin and then browse to Security in the menu in the upper left-hand corner, clicking on Dashboard.

Click on Single Sign On and then scroll down until you see EntityID. The EntityID is going to be everything after the = such as C034minsz9330 as follows

You can also find it by visiting the GooglePlay at where it’s listed as Organization ID.

I don’t think this key can be changed. Once you have the key, you can communicate with the Google API Gateway. For example:

--header 'Authorization: Bearer [$ACCESSTOKEN]' \
--header 'Accept: application/json' \

Disk Mount Conditioning In macOS

Here we go. The Disk Mount Conditioner “is a kernel provided service that can degrade the disk I/O being issued to specific mount points, providing the illusion that the I/O is executing on a slower device.” You won’t often find that the system decides to slow throughput to a device very often. But it happens, and equally as useful you can spoof a different type of device, quite helpful when troubleshooting. 

It’s like that. You can run the dmc command to control and view the status of the dmc service, at /usr/bin/dmc. To see how to use dmc, simply run dmc with the help verb:

/usr/bin/dmc help

Sucker M.C.s can see a list of mounts that can be controlled using the list verb:

  0: Faulty 5400 HDD

  1: 5400 HDD

  2: 7200 HDD

  3: Slow SSD



  6: PCIe 2 SSD

  7: PCIe 3 SSD

Is it live? Many of the above aren’t available, but if you look at your hard drive, you can check the status of it (or multiples if you have multiple supported controllers in your computer). To check the status simply use the list verb following the integer of your mount:

/usr/bin/dmc list

Pause. Once you have the list, you can check the status of each until you find the one you need. To check the status simply use the status verb following the integer of your mount:

/usr/bin/dmc status 1

If you run a status check against a mount that is not available (Peter Piper), you’ll get something similar to the following:

DISK_CONDITIONER_IOC_GET error: No such file or directory

If you run the status check on a supported mount (in my case rock box), you’ll get output similar to the following:

Disk Mount Conditioner: OFF

Profile: Custom

 Type: HDD

 Access time: 0 us

 Read throughput: 0 MB/s (unlimited)

 Write throughput: 0 MB/s (unlimited)

 I/O Queue Depth: 0

 Max Read Bytes: 0

 Max Write Bytes: 0

 Max Read Segments: 0

 Max Write Segments: 0

It’s tricky. Note that the Profile is listed as Custom. There’s a funky thing with this command, where if you want to see how the profile that is applied to a given device is configured – you use the characters  assigned to the list number:

/usr/bin/dmc show 1

Sorry, I talk too much. The output would be similar to the following:

Profile: 5400 HDD

 Type: HDD

 Access time: 26111 us

 Read throughput: 100 MB/s

 Write throughput: 100 MB/s

 I/O Queue Depth: 32

 Max Read Bytes: 33554432

 Max Write Bytes: 33554432

 Max Read Segments: 256

 Max Write Segments: 256

Walk this way – if you start the Disk Mount Conditioner for that device, you’d apply the profile of another in order to do so. So, let’s say you want to slow the device down with a given set of settings:

sudo dmc start 1

What’s it all about? Let’s say I wanted to spoof a PCIe 3 SSD. First I’d need to stop the conditioning service:

sudo dmc stop 1

Ooh, whatcha gonna do? Let’s run the start again with the mount followed by the profile index to invoke:

sudo dmc start 1 "PCIe 3 SSD"

Yah… And of course, to stop again, simply run the stop for the device again:

sudo dmc stop 1

But do be careful to revert back when you run dmc… Hard Times, right?

Happy 14th Birthday,

Originally started at the request of a publisher to support the first book I wrote, I guess I just never stopped… 14 years and 3,662 posts later I’m now writing my 21st book and constantly grateful for the opportunity the Apple Community has given me. Thank you so much for visiting and staying with me all this time (or just landing here on Google here and there). You’re the best community.

So a Happy New Year to all, and thanks for the thousands of comments to keep me busy!

Simple Stats in macOS

There’s a gem called iStats. It shows you fan speeds, cpu temp, battery cycle stats, and battery temp. Reason this came up for me is that I was asked a question on what the highs and lows were for computers to stay healthy in a remote sensor capacity. I typically try to keep computers above 25 (around -5 Celcius) and while the computer will shit itself off at 212 Fahrenheit (actually between 85 and 100 Celcius), it’s a good idea to keep it below 95 degrees (see for more information on preferred operating temperatures).

So you can use iStats to pull a few temperatures and then automatically send yourself an alert when the computer is getting to an inappropriate temperature (or automatically turn the heat or air on in a space that the computer is in. Anyway, to install iStats:

sudo gem install istats

And to then invoke iStats simply run the istats command: