diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt
index dec292a99c..2866a98c01 100644
--- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt
+++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt
@@ -168,10 +168,32 @@ abstract class FlowLogic<out T> {
         return result
     }
 
+    /**
+     * Flows can call this method to ensure that the active FlowInitiator is authorised for a particular action.
+     * This provides fine grained control over application level permissions, when RPC control over starting the flow is insufficient,
+     * or the permission is runtime dependent upon the choices made inside long lived flow code.
+     * For example some users may have restricted limits on how much cash they can transfer, or whether they can change certain fields.
+     * An audit event is always recorded whenever this method is used.
+     * If the permission is not granted for the FlowInitiator a FlowException is thrown.
+     * @param permissionName is a string representing the desired permission. Each flow is given a distinct namespace for these permissions.
+     * @param extraAuditData in the audit log for this permission check these extra key value pairs will be recorded.
+     */
+    @Throws(FlowException::class)
+    fun checkFlowPermission(permissionName: String, extraAuditData: Map<String,String>) = stateMachine.checkFlowPermission(permissionName, extraAuditData)
+
+
+    /**
+     * Flows can call this method to record application level flow audit events
+     * @param eventType is a string representing the type of event. Each flow is given a distinct namespace for these names.
+     * @param comment a general human readable summary of the event.
+     * @param extraAuditData in the audit log for this permission check these extra key value pairs will be recorded.
+     */
+    fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String,String>) = stateMachine.recordAuditEvent(eventType, comment, extraAuditData)
+
     /**
      * Override this to provide a [ProgressTracker]. If one is provided and stepped, the framework will do something
-     * helpful with the progress reports. If this flow is invoked as a subflow of another, then the
-     * tracker will be made a child of the current step in the parent. If it's null, this flow doesn't track
+     * helpful with the progress reports e.g record to the audit service. If this flow is invoked as a subflow of another,
+     * then the tracker will be made a child of the current step in the parent. If it's null, this flow doesn't track
      * progress.
      *
      * Note that this has to return a tracker before the flow is invoked. You can't change your mind half way
diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowStateMachine.kt b/core/src/main/kotlin/net/corda/core/flows/FlowStateMachine.kt
index e3deb0f14f..398c46c7be 100644
--- a/core/src/main/kotlin/net/corda/core/flows/FlowStateMachine.kt
+++ b/core/src/main/kotlin/net/corda/core/flows/FlowStateMachine.kt
@@ -10,6 +10,7 @@ import net.corda.core.serialization.CordaSerializable
 import net.corda.core.transactions.SignedTransaction
 import net.corda.core.utilities.UntrustworthyData
 import org.slf4j.Logger
+import java.security.Principal
 import java.util.*
 
 /**
@@ -18,14 +19,23 @@ import java.util.*
  * or via the Corda Shell [FlowInitiator.Shell].
  */
 @CordaSerializable
-sealed class FlowInitiator {
+sealed class FlowInitiator : Principal {
     /** Started using [net.corda.core.messaging.CordaRPCOps.startFlowDynamic]. */
-    data class RPC(val username: String) : FlowInitiator()
+    data class RPC(val username: String) : FlowInitiator() {
+        override fun getName(): String = username
+    }
     /** Started when we get new session initiation request. */
-    data class Peer(val party: Party) : FlowInitiator()
+    data class Peer(val party: Party) : FlowInitiator() {
+        override fun getName(): String = party.name.toString()
+    }
     /** Started as scheduled activity. */
-    data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator()
-    object Shell : FlowInitiator() // TODO When proper ssh access enabled, add username/use RPC?
+    data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator() {
+        override fun getName(): String = "Scheduler"
+    }
+    // TODO When proper ssh access enabled, add username/use RPC?
+    object Shell : FlowInitiator() {
+        override fun getName(): String = "Shell User"
+    }
 }
 
 /**
@@ -59,6 +69,10 @@ interface FlowStateMachine<R> {
     @Suspendable
     fun waitForLedgerCommit(hash: SecureHash, sessionFlow: FlowLogic<*>): SignedTransaction
 
+    fun checkFlowPermission(permissionName: String, extraAuditData: Map<String,String>)
+
+    fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String,String>)
+
     val serviceHub: ServiceHub
     val logger: Logger
     val id: StateMachineRunId
diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt
index 9074af2a28..fc788eb10c 100644
--- a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt
+++ b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt
@@ -55,6 +55,12 @@ class ProgressTracker(vararg steps: Step) {
     open class Step(open val label: String) {
         open val changes: Observable<Change> get() = Observable.empty()
         open fun childProgressTracker(): ProgressTracker? = null
+        /**
+         * A flow may populate this property with flow specific context data.
+         * The extra data will be recorded to the audit logs when the flow progresses.
+         * Even if empty the basic details (i.e. label) of the step will be recorded for audit purposes.
+         */
+        open val extraAuditData: Map<String, String> get() = emptyMap()
     }
 
     // Sentinel objects. Overrides equals() to survive process restarts and serialization.
diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
index 117309f677..641d568f26 100644
--- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
@@ -132,6 +132,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
         override val myInfo: NodeInfo get() = info
         override val schemaService: SchemaService get() = schemas
         override val transactionVerifierService: TransactionVerifierService get() = txVerifierService
+        override val auditService: AuditService get() = auditService
 
         // Internal only
         override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
@@ -177,6 +178,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
     lateinit var scheduler: NodeSchedulerService
     lateinit var flowLogicFactory: FlowLogicRefFactoryInternal
     lateinit var schemas: SchemaService
+    lateinit var auditService: AuditService
     val customServices: ArrayList<Any> = ArrayList()
     protected val runOnStop: ArrayList<Runnable> = ArrayList()
     lateinit var database: Database
@@ -295,6 +297,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
         schemas = makeSchemaService()
         vault = makeVaultService(configuration.dataSourceProperties)
         txVerifierService = makeTransactionVerifierService()
+        auditService = DummyAuditService()
 
         info = makeInfo()
         identity = makeIdentityService()
diff --git a/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt b/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt
index 52469576f7..d1a260a581 100644
--- a/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt
+++ b/node/src/main/kotlin/net/corda/node/services/RPCUserService.kt
@@ -15,6 +15,7 @@ interface RPCUserService {
 
 // TODO Store passwords as salted hashes
 // TODO Or ditch this and consider something like Apache Shiro
+// TODO Need access to permission checks from inside flows and at other point during audit checking.
 class RPCUserServiceImpl(override val users: List<User>) : RPCUserService {
     override fun getUser(username: String): User? = users.find { it.username == username }
 }
diff --git a/node/src/main/kotlin/net/corda/node/services/api/AuditService.kt b/node/src/main/kotlin/net/corda/node/services/api/AuditService.kt
new file mode 100644
index 0000000000..b9f53f9831
--- /dev/null
+++ b/node/src/main/kotlin/net/corda/node/services/api/AuditService.kt
@@ -0,0 +1,138 @@
+package net.corda.node.services.api
+
+import net.corda.core.flows.FlowLogic
+import net.corda.core.flows.StateMachineRunId
+import net.corda.core.serialization.SingletonSerializeAsToken
+import net.corda.core.utilities.ProgressTracker
+import java.security.Principal
+import java.time.Instant
+
+/**
+ * Minimum event specific data for any audit event to be logged. It is expected that the underlying audit service
+ * will enrich this to include details of the node, so that in clustered configurations the source node can be identified.
+ */
+sealed class AuditEvent {
+    /**
+     * The UTC time point at which the audit event happened.
+     */
+    abstract val timestamp: Instant
+    /**
+     * The responsible individual, node, or subsystem to which the audit event can be mapped.
+     */
+    abstract val principal: Principal
+    /**
+     * A human readable description of audit event including any permission check results.
+     */
+    abstract val description: String
+    /**
+     * Further tagged details that should be recorded along with the common data of the audit event.
+     * Examples of this might be trade identifiers, system error codes, or source IP addresses, which could be useful
+     * when searching the historic audit data for trails of evidence.
+     */
+    abstract val contextData: Map<String, String>
+}
+
+/**
+ * Sealed data class to mark system related events as a distinct category.
+ */
+data class SystemAuditEvent(override val timestamp: Instant,
+                            override val principal: Principal,
+                            override val description: String,
+                            override val contextData: Map<String, String>) : AuditEvent()
+
+/**
+ * Interface to mandate flow identification properties
+ */
+interface FlowAuditInfo {
+    /**
+     * The concrete type of FlowLogic being referenced.
+     * TODO This should be replaced with the fully versioned name/signature of the flow.
+     */
+    val flowType: Class<out FlowLogic<*>>
+    /**
+     * The stable identifier of the flow as stored with Checkpoints.
+     */
+    val flowId: StateMachineRunId
+}
+
+/**
+ * Sealed data class to record custom application specified flow event.
+ */
+data class FlowAppAuditEvent(
+        override val timestamp: Instant,
+        override val principal: Principal,
+        override val description: String,
+        override val contextData: Map<String, String>,
+        override val flowType: Class<out FlowLogic<*>>,
+        override val flowId: StateMachineRunId,
+        val auditEventType: String) : AuditEvent(), FlowAuditInfo
+
+/**
+ * Sealed data class to record the initiation of a new flow.
+ * The flow parameters should be captured to the context data.
+ */
+data class FlowStartEvent(
+        override val timestamp: Instant,
+        override val principal: Principal,
+        override val description: String,
+        override val contextData: Map<String, String>,
+        override val flowType: Class<out FlowLogic<*>>,
+        override val flowId: StateMachineRunId) : AuditEvent(), FlowAuditInfo
+
+/**
+ * Sealed data class to record ProgressTracker Step object whenever a change is signalled.
+ * The API for ProgressTracker has been extended so that the Step can contain some extra context data,
+ * which is copied into the contextData Map.
+ */
+data class FlowProgressAuditEvent(
+        override val timestamp: Instant,
+        override val principal: Principal,
+        override val description: String,
+        override val flowType: Class<out FlowLogic<*>>,
+        override val flowId: StateMachineRunId,
+        val flowProgress: ProgressTracker.Step) : AuditEvent(), FlowAuditInfo {
+    override val contextData: Map<String, String> get() = flowProgress.extraAuditData
+}
+
+/**
+ * Sealed data class to record any FlowExceptions, or other unexpected terminations of a Flow.
+ */
+data class FlowErrorAuditEvent(override val timestamp: Instant,
+                               override val principal: Principal,
+                               override val description: String,
+                               override val contextData: Map<String, String>,
+                               override val flowType: Class<out FlowLogic<*>>,
+                               override val flowId: StateMachineRunId,
+                               val error: Throwable) : AuditEvent(), FlowAuditInfo
+
+/**
+ * Sealed data class to record  checks on per flow permissions and the verdict of these checks
+ * If the permission is denied i.e. permissionGranted is false, then it is expected that the flow will be terminated immediately
+ * after recording the FlowPermissionAuditEvent. This may cause an extra FlowErrorAuditEvent to be recorded too.
+ */
+data class FlowPermissionAuditEvent(override val timestamp: Instant,
+                                    override val principal: Principal,
+                                    override val description: String,
+                                    override val contextData: Map<String, String>,
+                                    override val flowType: Class<out FlowLogic<*>>,
+                                    override val flowId: StateMachineRunId,
+                                    val permissionRequested: String,
+                                    val permissionGranted: Boolean) : AuditEvent(), FlowAuditInfo
+/**
+ * Minimal interface for recording audit information within the system. The AuditService is assumed to be available only
+ * to trusted internal components via ServiceHubInternal.
+ */
+interface AuditService {
+    fun recordAuditEvent(event: AuditEvent)
+}
+
+/**
+ * Empty do nothing AuditService as placeholder.
+ * TODO Write a full implementation that expands all the audit events to the database.
+ */
+class DummyAuditService : AuditService, SingletonSerializeAsToken() {
+    override fun recordAuditEvent(event: AuditEvent) {
+        //TODO Implement transformation of the audit events to formal audit data
+    }
+}
+
diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt
index 70b0e9dc91..a67796711a 100644
--- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt
+++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt
@@ -68,6 +68,7 @@ abstract class ServiceHubInternal : PluginServiceHub {
     abstract val schemaService: SchemaService
     abstract override val networkMapCache: NetworkMapCacheInternal
     abstract val schedulerService: SchedulerService
+    abstract val auditService: AuditService
 
     abstract val networkService: MessagingService
 
diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt
index 7f98089b2d..6b9693ed5f 100644
--- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt
+++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt
@@ -17,6 +17,8 @@ import net.corda.core.utilities.ProgressTracker
 import net.corda.core.utilities.UntrustworthyData
 import net.corda.core.utilities.debug
 import net.corda.core.utilities.trace
+import net.corda.node.services.api.FlowAppAuditEvent
+import net.corda.node.services.api.FlowPermissionAuditEvent
 import net.corda.node.services.api.ServiceHubInternal
 import net.corda.node.utilities.StrandLocalTransactionManager
 import net.corda.node.utilities.createTransaction
@@ -31,6 +33,8 @@ import java.sql.SQLException
 import java.util.*
 import java.util.concurrent.TimeUnit
 
+class FlowPermissionException(message: String) : FlowException(message)
+
 class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
                               val logic: FlowLogic<R>,
                               scheduler: FiberScheduler,
@@ -221,6 +225,37 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
         throw IllegalStateException("We were resumed after waiting for $hash but it wasn't found in our local storage")
     }
 
+    // TODO Dummy implementation of access to application specific permission controls and audit logging
+    override fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>) {
+        val permissionGranted = true // TODO define permission control service on ServiceHubInternal and actually check authorization.
+        val checkPermissionEvent = FlowPermissionAuditEvent(
+            serviceHub.clock.instant(),
+            flowInitiator,
+            "Flow Permission Required: $permissionName",
+            extraAuditData,
+            logic.javaClass,
+            id,
+            permissionName,
+            permissionGranted)
+        serviceHub.auditService.recordAuditEvent(checkPermissionEvent)
+        if (!permissionGranted) {
+            throw FlowPermissionException("User $flowInitiator not permissioned for $permissionName on flow $id")
+        }
+    }
+
+    // TODO Dummy implementation of access to application specific audit logging
+    override fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String,String>) {
+        val flowAuditEvent = FlowAppAuditEvent(
+                    serviceHub.clock.instant(),
+                    flowInitiator,
+                    comment,
+                    extraAuditData,
+                    logic.javaClass,
+                id,
+                eventType)
+        serviceHub.auditService.recordAuditEvent(flowAuditEvent)
+    }
+
     /**
      * This method will suspend the state machine and wait for incoming session init response from other party.
      */
diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt
index bec643b2e3..64bfb50cd6 100644
--- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt
+++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt
@@ -96,5 +96,13 @@ class InteractiveShellTest {
             get() = throw UnsupportedOperationException()
         override val flowInitiator: FlowInitiator
             get() = throw UnsupportedOperationException()
+
+        override fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>) {
+            // Do nothing
+        }
+
+        override fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String, String>) {
+            // Do nothing
+        }
     }
 }
\ No newline at end of file
diff --git a/node/src/test/kotlin/net/corda/node/services/MockServiceHubInternal.kt b/node/src/test/kotlin/net/corda/node/services/MockServiceHubInternal.kt
index 341a68662a..ffa3df6d9c 100644
--- a/node/src/test/kotlin/net/corda/node/services/MockServiceHubInternal.kt
+++ b/node/src/test/kotlin/net/corda/node/services/MockServiceHubInternal.kt
@@ -60,7 +60,7 @@ open class MockServiceHubInternal(
         get() = flowFactory ?: throw UnsupportedOperationException()
     override val schemaService: SchemaService
         get() = schemas ?: throw UnsupportedOperationException()
-
+    override val auditService: AuditService = DummyAuditService()
     // We isolate the storage service with writable TXes so that it can't be accessed except via recordTransactions()
     private val txStorageService: TxWritableStorageService
         get() = storage ?: throw UnsupportedOperationException()