diff --git a/djangoldp_circle/migrations/0008_circlemember_is_admin.py b/djangoldp_circle/migrations/0008_circlemember_is_admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..9fdccd485dd3f125b252a67b40b563947657eca1
--- /dev/null
+++ b/djangoldp_circle/migrations/0008_circlemember_is_admin.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2019-11-21 16:56
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('djangoldp_circle', '0007_auto_20191112_1251'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='circlemember',
+            name='is_admin',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/djangoldp_circle/models.py b/djangoldp_circle/models.py
index 9ea673086e10e0f1849b92a4db6e9e34adc8e8d9..ab22d577b8b29141d5b628ece46d40726ec3f924 100644
--- a/djangoldp_circle/models.py
+++ b/djangoldp_circle/models.py
@@ -5,6 +5,7 @@ from django.db import models
 from django.db.models.signals import pre_save, post_save
 from django.dispatch import receiver
 from djangoldp.models import Model
+from .permissions import CirclePermissions, CircleMemberPermissions
 
 STATUS_CHOICES = [
     ('Public', 'Public'),
@@ -27,6 +28,7 @@ class Circle(Model):
         auto_author = 'owner'
         owner_field = 'owner'
         nested_fields = ["team", 'members']
+        permission_classes = [CirclePermissions]
         anonymous_perms = ["view"]
         authenticated_perms = ["inherit", "add"]
         owner_perms = ["inherit", "change", "delete"]
@@ -36,17 +38,32 @@ class Circle(Model):
     def __str__(self):
         return self.name
 
+    def get_admins(self):
+        return self.members.filter(is_admin=True)
+
 
 class CircleMember(Model):
     circle = models.ForeignKey(Circle, on_delete=models.CASCADE, related_name='members')
     user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="circles")
+    is_admin = models.BooleanField(default=False)
 
     def __str__(self):
         return str(self.circle.name) + " - " + str(self.user.name())
 
+    def save(self, *args, **kwargs):
+        # cannot be duplicated CircleMembers
+        if not self.pk and CircleMember.objects.filter(circle=self.circle, user=self.user).exists():
+            return
+
+        # owners must be an admin
+        if not self.is_admin and self.circle.owner == self.user:
+            self.is_admin = True
+
+        super(CircleMember, self).save(*args, **kwargs)
+
     class Meta:
-        # TODO: As a auth I can still add someone else.
         container_path = "circle-members/"
+        permission_classes = [CircleMemberPermissions]
         authenticated_perms = ["view", "add"]
         owner_perms = ["view", "add", "delete"]
         # auto_author = "user"
@@ -75,6 +92,7 @@ def set_jabberid(sender, instance, **kwargs):
         instance.jabberRoom = True
 
 @receiver(post_save, sender=Circle)
-def set_ower_as_member(instance, created, **kwargs):
+def set_owner_as_member(instance, created, **kwargs):
+    # add owner as an admin member, if they've not already been added
     if created and not instance.members.filter(user=instance.owner).exists():
-        CircleMember.objects.create(user=instance.owner, circle=instance)
+        CircleMember.objects.create(user=instance.owner, circle=instance, is_admin=True)
\ No newline at end of file
diff --git a/djangoldp_circle/permissions.py b/djangoldp_circle/permissions.py
new file mode 100644
index 0000000000000000000000000000000000000000..869d1224aefd031a455e726d64819f3fcd38fa81
--- /dev/null
+++ b/djangoldp_circle/permissions.py
@@ -0,0 +1,87 @@
+from djangoldp.permissions import LDPPermissions
+
+
+# auxiliary function tests user is an admin for specified circle
+def is_user_admin_of_circle(user, circle):
+    from .models import CircleMember
+
+    if user.is_anonymous:
+        return False
+
+    try:
+        circle_member = CircleMember.objects.get(user=user, circle=circle)
+        return circle_member.is_admin
+
+    except:
+        return False
+
+
+class CirclePermissions(LDPPermissions):
+    def has_permission(self, request, view):
+        # request on an existing resource - this will be reviewed by has_object_permission
+        if request.method == 'PATCH' or request.method == 'DELETE' or request.method == 'PUT':
+            return True
+
+        return super().has_permission(request, view)
+
+    def has_object_permission(self, request, view, obj):
+        from .models import CircleMember
+
+        # admins have full permissions
+        if is_user_admin_of_circle(request.user, obj):
+            return True
+
+        # other members can perform GET only
+        if obj.status != 'Public':
+            if request.user.is_anonymous:
+                return False
+
+            if not CircleMember.objects.filter(user=request.user, circle=obj).exists():
+                return False
+
+            if request.method != 'GET':
+                return False
+
+        return super().has_object_permission(request, view, obj)
+
+
+class CircleMemberPermissions(LDPPermissions):
+    def has_permission(self, request, view):
+        # request on an existing resource - this will be reviewed by has_object_permission
+        if request.method == 'PATCH' or request.method == 'DELETE' or request.method == 'PUT':
+            return True
+
+        return super().has_permission(request, view)
+
+    def has_object_permission(self, request, view, obj):
+        from .models import CircleMember
+
+        # admins have full permissions
+        if is_user_admin_of_circle(request.user, obj.circle):
+            if request.method == 'DELETE':
+                # I cannot remove myself if I am the last admin
+                if obj.pk == request.user.pk:
+                    if obj.circle.get_admins().count() == 1:
+                        return False
+
+                # I cannot remove another admin
+                elif obj.is_admin:
+                    return False
+
+            return True
+
+        # I can remove myself
+        if obj.user.pk == request.user.pk:
+            return True
+
+        # anyone can perform GET if it's public
+        if obj.circle.status == 'Public':
+            if request.method == 'GET':
+                return True
+
+        # private circles, members can GET/POST
+        elif obj.circle.status == 'Private':
+            if request.method == 'GET' or request.method == 'POST':
+                return CircleMember.objects.filter(user=request.user, circle=obj.circle).exists()
+
+        return super().has_object_permission(request, view, obj)